day002.zip
[TOC]

静态成员

  1. 静态成员变量
    C++中作用域:
    1. 复合语句 \ 局部作用域
    2. 函数作用域 /
    3. 文件作用域 - 全局作用域
    4. 类作用域
    5. 命名空间

静态成员变量是在类内定义的, 但它本质上属于全局变量. 但是它的作用域是属于类域.
意思是:

  1. 静态成员变量的生存周期和全局变量是一样(从程序运行到程序结束)(成员变量的生存周期是和对象一起. 对象被构造出来,成员变量拥有内存, 对象被析构,成员变量就是去内存. 而对象又分为局部对象,全局对象,堆空间对象), 因此静态成员变量不属于对象中的一部分(也就是说对象的内存中不包含静态成员变量)
  2. 静态成员变量的作用域是属于类域(影响着对静态成员变量的使用.), 当需要在类外使用静态成员变量的时候, 可以通过两种方法来访问:
    1. 通过类名, 例如: 类名::静态成员变量名
    2. 通过对象, 例如: 对象.静态成员变量名 ```c class MyClass{

public: // 1. 声明静态成员变量(加一个static关键字) static int m_nNum;
}; // 2. 必须要在类外定义静态成员变量 // 需要在变量名前加上作用域说明该变量属于类的. // 如果没有定义, 就会报错: 无法解析的外部符号m_nNum int MyClass::m_nNum; int g_nNum; int main() { // 使用静态成员变量 MyClass::m_nNum3 = 10; // 比较两者的地址,就会发现它们在相近的内存中. printf(“g_nNum=%p m_nNum3=%p\n”, &g_nNum, &MyClass::m_nNum3 ); }

  1. 2. 静态成员函数
  2. ```c
  3. class MyClass{
  4. int m_nNum1;
  5. static int m_nNum;
  6. public:
  7. //
  8. // 定义静态成员函数:加上static关键字
  9. // 1. 静态成员函数的本质相当于类外的一个普通
  10. // 函数(在函数内部没有this指针)
  11. // 2. 静态成员函数的作用域属于类的作用域(在
  12. // 静态成员函数内部, 通过类对象或对象指针
  13. // 可以访问到全部的成员变量和成员函数)
  14. static void staticFun() {
  15. //this->m_nNum1 = 0;// 1. 静态成员函数没有this指针
  16. MyClass obj;
  17. obj.m_nNum1 = 0;// 2.通过对象可以访问到私有成员
  18. m_nNum3 = 100; // 3. 当然也可以直接访问静态成员变量
  19. }
  20. };
  21. int MyClass::m_nNum = 0;
  22. int main()
  23. {
  24. // 调用静态成员函数:
  25. MyClass::staticFun();
  26. }

友元

  1. C++提供了三大访问控制权限用于控制类外,类内,子类内对成员的访问的控制.
  2. 友元就是一个类对某个对象授予所有的访问控制权限
    1. 例如MyClass有一个私有的成员变量m_nNum
    2. 在main函数中是不能直接通过对象来访问的. 但是通过友元授权,main也能直接通过对象来访问到这个类的所有成员.
  3. 友元能够授予权限的对象:
    1. 友元普通函数 : 将类的访问权限全部授予一个普通的函数 ```c class MyClass{ int m_nNum; static int m_staticNum; // 使用friend关键字将某个普通函数的函数头放在类内声明 // 这个普通函数就能成为本类友元普通函数. friend int main(); }; int MyClass::m_staticNum;

int main(){ MyClass obj; obj.m_nNum=0;// 成为友元之后可以访问私有成员 MyClass::m_staticNum=0; }

  1. 1. 友元类 : 将本类的访问权限全部授予给另一个类(在另一个类的所有成员函数中都能直接访问到授权类的所有成员)
  2. ```c
  3. class MyClass{
  4. int m_nNum;
  5. static int m_staticNum;
  6. // 声明一个友元类: friend class 类名;
  7. // 作用: 该类所有成员函数都能访问本类的
  8. // 私有成员.
  9. friend class Class2;
  10. };
  11. int MyClass::m_staticNum;
  12. class Class2{
  13. public:
  14. void fun(MyClass& obj){
  15. // 声明友元类之后, 就能在成员函数中
  16. // 去访问MyClass类的私有变量
  17. obj.m_nNum = 0;
  18. }
  19. };
  1. 友元成员函数 : 将本类的访问权限全部授予给另一个类某个成员函数(只有被授权的成员函数能够访问所有成员,没有被授权的成员函数访问不了)
    1. 互相引用的问题 : 在A类中使用了B类, B类又使用了A类. 类的声明就无法正常声明了. ```c class MyClass2 { public: void fun1(); void fun2(); };

class MyClass1 { int m_nNum; public: // 声明友元成员函数 friend void MyClass2::fun1(); };

void MyClass2::fun1() { MyClass1 obj; // 访问私有成员变量 obj.m_nNum = 0; } void MyClass2::fun2() { MyClass1 obj; // 访问私有成员变量, 但fun2并没有被声明成友元成员函数. 因此访问失败. obj.m_nNum = 0; }

  1. 1. 运算符重载有关
  2. <a name="9bace9a3"></a>
  3. # 运算符重载
  4. 1. C++中有很多会被自动调用的代码
  5. 1. 构造一个对象, 构造函数被调用了
  6. 1. 当一个对象被销毁的时候, 析构函数被调用
  7. 1. 当编译器需要进行隐式转换时, 转换构造被调用了.
  8. 1. 当一个对象被使用了运算符(+,-,*,/,%....)的时候
  9. 2. 无论是在C还是在c++中, 能够直接使用运算符进行运算的数据类型一般只有基本数据类型.
  10. 1. 意思就是, 无法直接将一个数组使用`+`,`-`,`*`等运算符, 结构体变量, 类对象同样如此.
  11. 1. 如果想要将非基本数据类型的变量使用运算符,一般需要通过另一些运算符来得到基本数据类型,然后再直接使用运算符.
  12. 1. 例如一个结构体变量,不能直接使用 +运算符, 但是通过`.``->`可以从结构体变量中得到一个字段(基本数据类型), 这个基本数据类型就能使用运算符了
  13. 3. C++中. 提供一种特殊的成员函数. 这些成员函数的名字都有一个相同的前缀,都叫`operator`, 他们一般都有一个不同的后缀, 不同的后缀就是运算符. 例如:`operator+` , `operator-`, 这些函数在使用的时候, 一般有两种形式:
  14. 1. `对象.operator+(5);`
  15. 1. `对象 + 5`
  16. ```c
  17. string strobj2;
  18. strobj2.operator+=("123");
  19. strobj2 += "123";
  1. 运算符重载的意义:
    1. 为了能够让对象直接使用运算符.
  2. 运算符重载的本质:
    1. 一个运算符就是一个函数调用. 如果运算符是一个单目运算符,这个函数调用就不需要传递参数. 如果运算符是双目运算符, 那么运算符的左操作数就是对象自身, 右操作数就是函数中的形参1.
    2. 当对一个类对象使用运算符的时候, c++编译器在类中查找一个函数(以operator开头的函数),如果没有,就报错了. 如果找到了就调用该函数.
      1. 调用函数的传参办法:
        1. 单目运算符 : 不用传递. 对象自身就是单目运算符的操作数, 例如: ++obj;
        2. 双目运算符:
          1. 成员函数版本: 对象默认作为左操作数, 右操作数作为参数1. 例如, obj + 5 会被转化成:obj.operator+(5); 而5+obj就无法转化.
          2. 友元函数版本: 根据左操作数作为形参1,右操作数作为形参2 , 例如: obj+5 会被转化成: operator(obj,5), 5 + obj 会被转化成: operator(5,obj)

运算符重载的使用

单目运算符的重载

  1. class Numer {
  2. int m_nNum = 0;
  3. public:
  4. // 前置++
  5. Numer& operator++() {
  6. ++m_nNum;
  7. return *this;
  8. }
  9. // 后置++
  10. Numer operator++(int) {
  11. // 先保存+1之前的值
  12. Numer t;
  13. t.m_nNum = this->m_nNum;
  14. // 自身自增
  15. ++m_nNum;
  16. // 返回自增前的值
  17. return t;
  18. }
  19. // 前置--
  20. void operator--() {
  21. }
  22. // 后置--
  23. void operator--(int) {
  24. }
  25. };
  26. int main()
  27. {
  28. Numer obj;
  29. Numer obj2 = obj++;// 转换成运算符重载函数:obj.operator++();
  30. obj.operator++();
  31. // 如果是后置++, 编译器只会找operator++(int)
  32. // 的成员函数 , 形参中的int只是为了区分前置和后置,
  33. // 并没有传递实际的参数.
  34. obj++;// 转换成运算符重载函数:obj.operator++(int);
  35. --obj;// 转换成运算符重载函数:obj.operator--();
  36. obj--;// 转换成运算符重载函数:obj.operator--(int);
  37. ++++++obj; //转换成:obj.operator++().operator++().operator++();
  38. }

双目运算符的重载

  1. 成员函数版本

    1. class Numer{
    2. int m_nNum;
    3. public:
    4. Numer():m_nNum(){}
    5. // 转换构造函数
    6. Numer(int n) :m_nNum(n) {}
    7. Numer(const char* pStr) {
    8. sscanf_s(pStr, "%d", &m_nNum);
    9. }
    10. Numer operator+(const Numer& n) {
    11. // 将自身拷贝到t
    12. Numer t(*this);
    13. t.m_nNum = this->m_nNum + n.m_nNum;
    14. return t;
    15. }
    16. };
  2. 友元函数版本
    对于成员函数版本的运算符重载有一个缺陷: 运算符的左操作数必须是对象才能完成调用, 对于左操作数不是对象的情况,就无法调用了,例如:obj3 = 123 + obj3 ;会报错.
    要解决这个问题, 就需要改变左操作数只能是对象的现状
    改变的方法就是使用友元版本的运算符重载.
    友元版本的运算符重载在定义运算符重载函数的时候, 形参中必须指定两个操作数. 参数1默认是左操作数, 参数2默认是右操作数.(单目运算符默认不使用友元版本) ```c class Numer { int m_nNum = 0; public:

    // 默认构造 Numer():m_nNum(){}

    // 转换构造函数 Numer(int n) :m_nNum(n) {} Numer(const char* pStr) {

    1. sscanf_s(pStr, "%d", &m_nNum);

    }

    // 成员函数版本 // 只能在形参中指定右操作数 Numer operator+(const Numer& n) {

    1. // 将自身拷贝到t
    2. Numer t(*this);
    3. t.m_nNum = this->m_nNum + n.m_nNum;
    4. return t;

    }

    // 友元函数版本 // 必须在形参中指定运算符的两个操作数 friend Numer operator+(const Numer& left,

    1. const Numer& right)

    {

    1. Numer t(left);
    2. t.m_nNum += right.m_nNum;
    3. return t;

    } };

int main() { Numer obj3;

  1. obj3 = obj3 + "123456";
  2. // obj3.operator+(Numer("123456"));
  3. // obj3 += "123456";
  4. obj3 = 123 + obj3;//转换成: operator+(Number(123) , obj3);

}

  1. <a name="8f3d2838"></a>
  2. #### 对cout和cin的支持
  3. ```c
  4. class Numer {
  5. int m_nNum = 0;
  6. public:
  7. // 默认构造
  8. Numer():m_nNum(){}
  9. // 转换构造函数
  10. Numer(int n) :m_nNum(n) {}
  11. Numer(const char* pStr) {
  12. sscanf_s(pStr, "%d", &m_nNum);
  13. }
  14. friend ostream& operator<<(ostream& o,
  15. const Numer& obj)
  16. {
  17. o << obj.m_nNum;
  18. return o;
  19. }
  20. friend istream& operator>>(istream& i,
  21. const Numer& obj)
  22. {
  23. i >> obj.m_nNum;
  24. return i;
  25. }
  26. };
  27. int main()
  28. {
  29. Numer obj3;
  30. obj3 = obj3 + "123456";
  31. // obj3.operator+(Numer("123456"));
  32. // obj3 += "123456";
  33. obj3 = 123 + obj3;//operator(123 , obj3);
  34. // 对cout和cin的支持
  35. cout << obj3 << " sdfsadkf" << endl;
  36. //可以转化成 : cout.operator<<(obj3);// 成员函数版本
  37. //也可以转化成 : operator(count, obj3);// 友元函数版本
  38. cin >> obj3;// operator(cin,obj3);
  39. }