内联成员函数和重载成员函数

  • 内联成员函数
    • inline + 成员函数
    • 整个函数体出现在类定义内部
  • 重载成员函数
    • 成员函数 – 带缺省参数
    • 避免缺省参数函数和无参函数的二义性

构造函数

  • 一般构造函数

  • 复制构造函数

    • 只有一个参数, 即对同类对象的引用

    • T :: T(T&)T :: T(const T&)

    • 若未定义, 编译器生成默认复制构造函数(完成复制功能)

      1
      2
      3
      4
      5
      6
      class Complex {
      private:
      double real, imag;
      };
      Complex c1; //调用缺省无参构造函数
      Complex c2(c1); //调用缺省的复制构造函数, 将c2初始化成和c1一样
    • 什么时候起作用

      1. 当用一个对象去初始化同类的另一个对象时

        1
        2
        Complex c2(c1);
        Complex c2 = c1; //初始化语句, 非赋值语句
      2. 如果某函数有一个参数是类A的对象, 那么该函数被调用时, 类A的复制构造函数将被调用

        1
        2
        3
        4
        5
        6
        void Func(A a1){}
        int main() {
        A a2;
        Func(a2);
        return 0;
        }
      3. 如果函数的返回值是类A的对象时, 则函数返回时, A的复制构造函数被调用

        1
        2
        3
        4
        5
        6
        7
        8
        A Func() {
        A b(4);
        return b; //返回的b未必就是上面定义的b, 取决于A的复制构造函数
        }
        int main() {
        cout << Func().v << endl;
        return 0;
        }
  • 类型转换构造函数

    • 实现类型的自动转换

    • 只有一个参数

    • 赋值时, 编译系统会自动调用, 建立一个临时对象/临时变量

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      class Complex {
      public:
      double real, imag;
      Complex(int i) { //类型转换构造函数
      real = i; imag = 0;
      }
      Complex(double r, double i) {
      real = r; imag = i;
      }
      };
      int main() {
      Complex c1(7, 8);
      Complex c2 = 12; //初始化语句, 调用构造函数
      c1 = 9; //赋值语句, 调用类型转换构造函数, 被自动转换成一个临时Complex对象
      return 0;
      }

析构函数

  • 没有参数和返回值

  • 一个类最多只有一个析构函数

  • 对象消亡时自动调用

  • 未定义析构函数时, 编译器自动生成一个没用的析构函数

image-20250120221934716

静态成员变量和静态成员函数

  • static

  • 静态成员变量所有对象共享

  • sizeof运算符不会计算静态成员变量

    1
    2
    3
    4
    5
    class CMyclass {
    int n;
    static int s;
    };
    //sizeof(CMyclass) == 4
  • 静态成员函数不具体作用于某个对象, 不需要通过对象就能访问

    • 类名::成员名
    • 对象名.成员名
    • 指针->成员名
    • 引用.成员
  • 易于理解和维护

  • 必须在定义类的文件中对静态成员变量进行一次说明或初始化, 否则编译能通过, 链接不能通过

  • 在静态成员函数中, 不能访问非静态成员变量, 也不能调用非静态成员函数

成员对象和封闭类概念

  • 成员对象: 一个类的成员变量是另一个类的对象

  • 包含成员对象的类叫做封闭类

  • 调用顺序

    • 当封闭类对象生成时
      1. 执行所有成员对象的构造函数
      2. 执行封闭类的构造函数
    • 成员对象的构造函数调用顺序
      • 和成员对象在类中的说明顺序一致
      • 与在成员初始化列表中出现的顺序无关
    • 当封闭类的对象消亡时
      1. 先执行封闭类的析构函数
      2. 执行成员对象的析构函数

友元

  • 友元函数

    • 一个类的友元函数可以访问该类的私有成员

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      class CCar; //提前声明CCar类, 以便后面CDriver类使用
      class CDriver {
      public:
      void ModifyCar(CCar *pCar); //改装汽车
      };
      class CCar {
      private:
      int price;
      friend int MostExpensiveCar(CCar cars[], int total); //声明友元
      friend void CDriver::ModifyCar(CCar *pCar); //声明友元
      };
  • 友元类

    • A是B的友元类, A的成员函数可以访问B的私有成员
    • 友元类之间的关系不能传递, 不能继承

this指针

  • 非静态成员函数中可以直接使用this来代表指向该函数作用的对象的指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class A {
    int i;
    public:
    void Hello() { cout << "hello" << endl; }
    //编译为 void Hello(A *this) { cout << "hello" << endl; }
    };
    int main() {
    A *p = NULL;
    p->Hello(); //编译为 Hello(p);
    } //输出: hello

常量对象、常量成员函数和常引用

  • 常量成员函数(在函数后面加const)执行期间不应修改其所作用的对象, 因此, 在常量成员函数中不能修改成员变量的值 (静态成员变量除外), 也不能调用同类的非常量成员函数 (静态成员函数除外)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class CTest {
    private:
    int n;
    public:
    CTest() { n = 1; }
    int GetValue() const { return n; }
    int GetValue() { return 2 * n; }
    };
    int main() {
    const CTest objTest1;
    CTest objTest2;
    cout << objTest1.GetValue() << "," << objTest2.GetValue(); //输出: 1,2
    return 0;
    }
  • 常引用

    • 对象作为函数的参数时, 生成参数需要调用复制构造函数, 效率比较低, 用指针作参数, 代码不好看

    • 可以用对象的引用作为参数, 但是有风险, 函数中修改形参, 实参也会变

      1
      2
      3
      4
      5
      6
      class Sample {
      ...
      };
      void Print(const Sample &o) {
      ...
      }