首页 > C/C++ > 侯捷-c++面向对象程序设计-学习笔记-10-类的static成员、namespace和模板简介

侯捷-c++面向对象程序设计-学习笔记-10-类的static成员、namespace和模板简介

2020年8月13日
分类: C/C++ 标签: ,

1,static成员变量

c++类中的非static修饰的成员变量在每个对象中都各自拥有一份,而static修饰的成员变量则只有一份,这种成员变量被同一类的所有对象共有。

【一个变量为多个对象共有,这种情况在多线程环境下是不安全的,需要考虑数据同步问题】

#include <iostream>
using namespace std;

class Account{
  public:
    static double m_rate;
};

//static成员变量需要在类外单独定义
double Account::m_rate = 8.0;

int main(){
  //通过类名访问static成员变量
  Account::m_rate = 6.0;
  cout << Account::m_rate << endl;       //6

  //通过对象访问static成员变量
  Account a;
  cout << a.m_rate << endl;              //6
  a.m_rate = 7.0;
  cout << a.m_rate << endl;               //7

  Account b;
  cout << b.m_rate << endl;               //7
  
  return 0;
}

如上边代码所示,static成员变量需要在类的声明之外单独定义。

 

2,static成员函数

c++类中可以定义一种被static修饰的函数,这类函数形参中【不含隐式的this指针】,这意味着static成员函数不能访问属于某个对象的(非static)成员变量,而【只能访问static成员变量】。

#include <iostream>
using namespace std;

class CS{
  public:
    static void print(){
        cout << "static" << endl;
        cout << m_s << endl;
        //cout << m_i << endl;
        //error: invalid use of member 'CS::m_i' in static member function
    }
    
  public:
    static int m_s;
    int m_i;
};
int CS::m_s = 0;

int main()
{
  //通过类名调用static成员函数
  CS::print();
  
  CS a;
  a.m_s = 1;
  //通过对象调用static成员函数
  //但是static成员函数无法访问对象拥有的非static成员变量m_i
  a.print();
  
  return 0;
}

static成员函数和static成员变量有一个典型的应用场景就是单例(Singleton)模式:类只有一个实例化对象。

【如何确保一个类只能有一个实例对象呢?首先,不允许用户随意创建对象,干脆直接禁止用户手动创建对象,也就是禁止用户调用类的构造函数。其次,保证可以创建类的一个唯一的对象,这可以通过static成员变量实现。最后,需要提供用户一个接口,可以访问这个唯一的对象,static成员函数正好可以实现这个需求。】

下边是一个单例的实现例子:

class Singleton{
  public:
    static Singleton& getInstance(){return s;}
  private:
    Singleton(){}
    Singleton(const Singleton&){}
    static Singleton s;
};

int main()
{
  Singleton::getInstance();
  return 0;
}

这个例子中,我们通过Singleton::getInstance()来获取类的唯一对象。

如果这个接口从未被调用,内存中还是会有一份Singleton对象存在。所以可稍作优化:

class Singleton{
  public:
    static Singleton& getInstance();
  private:
    Singleton(){}
    Singleton(const Singleton&){}
};

Singleton& Singleton::getInstance(){
  static Singleton a;
  return a;
}

int main()
{
  Singleton::getInstance();
  return 0;
}

把static成员变量修改为局部static变量,这样只在getInstance被调用后才会创建一个Singleton的对象,并且后续调用不会重复创建。

【需要注意一点:这种通过static局部对象创建单例的方式在c++98中不是线程安全的,只在c++11之后才是线程安全的。】

3,cout为什么可以输出各种类型的数据?

cout派生自ostream,而ostream派生自ios。ios对operator <<进行了诸多重载,支持输出各种基本类型数据。

 

4,类模板

如果一个类的某些成员变量可以是多种类型,我们可以通过类模板实现类的复用。

类模板将成员变量的类型参数化,可以在编译时自动绑定指定的数据类型,这种编译时执行的类型绑定称为静态绑定。

// Example program
#include <iostream>
#include <string>
using namespace std;

template <class T>
class TT{
  public:
    TT(const T& _l, const T& _r): l(_l), r(_r){}
    T sum(){return l+r;}
    
  private:
    T l,r;
};

int main(){
    TT<int> ti(3,4);
    cout << ti.sum() << endl;
    
    TT<string> ts("Hello", "World");
    cout << ts.sum() << endl;
    
    return 0;
}

//7
//HelloWorld

类模板在使用时需要在<>中显示指定参数的类型。

 

5,函数模板

函数模板和类模板相似,也是为了使函数的功能可以应用在不同类型的数据上,节省编码工作量。

#include <iostream>
#include <string>
using namespace std;

template <class T>
T sum(const T& l, const T& r){ return l+r;}

int main(){
    cout << sum(3,4) << endl;
    
    string s1("Hello"), s2("World");
    cout << sum(s1, s2) << endl;
    
    return 0;
}

这个例子和上边类模板实现的功能相同。

函数模板不需要显示指定参数的类型,编译器会自动进行类型推导。

【类模板和函数模板对于T是有一定要求的,T需要满足代码中的相关调用。比如例子中的相加操作,T类型本身要支持+运算符。】

 

6,namespace(命名空间)

namespace是为了避免项目中的命名冲突。

 

————————-笔记分割线——————–

【侯捷老师推荐的书籍】

您可能需要这些【参考资料】:
零一积流|IT参考 原创文章,转载请注明出处: http://www.it-refer.com/2020/08/13/houjie-cplusplus-object-oriented-programming-10-static-class-member-namespace-template


本文的评论功能被关闭了.