构造函数的基本格式

声明

只需要记住一点,构造函数名=类名

class MyClass {
public:
    MyClass() { }  
};

类外定义

类名 : : 类名 ( ) { } 类名::类名()\{ \} 类名::类名(){}

MyClass::MyClass() { }  

初始化成员变量的构造函数

类名 ( 参数列表 ) : 成员变量名 ( 用于初始化的参数 ) , 成员变量名 ( 用于初始化的参数 ) { } 类名(参数列表):成员变量名(用于初始化的参数),成员变量名(用于初始化的参数)\{ \} 类名(参数列表):成员变量名(用于初始化的参数),成员变量名(用于初始化的参数){}

class Student {
private:
    string name;
    int age;
    double score;
public:    
    Student(const string& n, int a, double s) : name(n), age(a), score(s) {}  
};

注意:成员的初始化顺序只取决于它们在类中声明的顺序,与在初始化列表中的书写顺序无关。

初始化成员对象的构造函数

类名 ( 参数列表 ) : 成员对象名 ( 用于初始化的参数 ) , 成员变量名 ( 用于初始化的参数 ) { } 类名(参数列表):成员对象名(用于初始化的参数),成员变量名(用于初始化的参数)\{ \} 类名(参数列表):成员对象名(用于初始化的参数),成员变量名(用于初始化的参数){}

class Parent {
private:
    string fatherName;
    string motherName;
    string phone;
public:    
    // Parent 类的带参构造函数
    Parent(const string& father, const string& mother, const string& ph) 
        : fatherName(father), motherName(mother), phone(ph) {
        cout << "Parent 带参构造函数: " << father << ", " << mother << endl;
    }   
};

class Student {
private:
    string name;
    int age;
    double score;
    Parent parents;  // 成员对象 - Parent类的实例
public:    
    
    // 显式初始化 Parent 成员对象
    Student(const string& n, int a, double s,  const string& father, const string& mother, const string& phone)
     : name(n), age(a), score(s), parents(father, mother, phone) {
        cout << "Student 构造函数(传递信息)" << endl;
    }
    
    // 直接传递 Parent 对象---使用复制构造函数
    Student(const string& n, int a, double s, const Parent& p) 
        : name(n), age(a), score(s), parents(p) {
        cout << "Student 构造函数(传递Parent对象)" << endl;
    }
};

调用基类构造函数的派生类构造函数

派生类名 ( 参数列表 ) : 基类名 ( 参数列表 ) , 成员对象名 ( 用于初始化的参数 ) , 成员变量名 ( 用于初始化的参数 ) { } 派生类名(参数列表):基类名(参数列表),成员对象名(用于初始化的参数),成员变量名(用于初始化的参数)\{ \} 派生类名(参数列表):基类名(参数列表),成员对象名(用于初始化的参数),成员变量名(用于初始化的参数){}

class Person {
private:
    string name;
    int age;
public:
    // 构造函数
    Person(const string& n, int a) : name(n), age(a) { }

};

class Student : public Person {  // public继承
private:
    double score;
    string studentId; 
public:
    // 构造函数
    Student(const string& n, int a,  double s, const string& sid) 
        : Person(n, a, id_num), score(s), studentId(sid) {
    }
};

函数重载情况下的构造函数

根据函数重载的性质,同名的函数可以根据参数的不同种类从而调用不同的函数,构造函数也是如此,可以有多个构造函数。

class Student {//函数重载
private:
    string name;
    int age;
    double score;
public:
    // 多个构造函数 - 参数列表不同
    Student() : name("未知"), age(0), score(0) {}  // 无参
    
    Student(const string& n) : name(n), age(0), score(0) {}  // 一个参数
    
    Student(const string& n, int a) : name(n), age(a), score(0) {}  // 两个参数
    
    Student(const string& n, int a, double s) : name(n), age(a), score(s) {}  // 三个参数
};

默认构造函数(只能有一个)

默认构造函数:可以不带任何参数调用的构造函数。分为以下三种情况。
调用时机:没有传参

1、编译器生成的默认构造函数

在代码当中没有写明构造函数

//无

2、显式无参默认构造函数

class MyClass {
public:
    MyClass() { }  // 显式无参默认构造函数
};

3、所有参数都有默认值的默认构造函数

class MyClass {
public:
    MyClass(int x = 0, double y = 0.0) { }  
};

只能有一个

根据函数重载的性质,同名的函数可以根据参数的不同种类从而调用不同的函数,构造函数也是如此,可以有多个构造函数。**但是,默认构造函数只能有一个。**也就是说,2可以出现,3也可以出现,但是2和3同时出现就会报错。

class MyClass {
public:
    MyClass() { }                    // 默认构造函数
    MyClass(int x = 0) { }           // 这也是默认构造函数!冲突!
};

复制/拷贝构造函数

复制构造函数:用一个已存在的对象来初始化一个新对象(拷贝一个副本)
如果没有显示定义则会生成默认的复制构造函数
调用时机:用已有对象初始化新对象(传入同类对象)、将对象作为函数参数传递、函数返回值是一个对象
类名 ( c o n s t   类名 &   对象名 ) 类名(const \ \ 类名\& \ \ 对象名) 类名(const  类名&  对象名)
括号里的是指向类对象的常量引用

class MyClass {
public:
    // 复制构造函数
    MyClass(const MyClass& other) {
    }
};

移动构造函数(Move Constructor)

移动语义

Q:什么是移动语义?
A:实际文件还留在原来位置,而只是修改记录。
Q:在什么情况下触发移动语义?
A:在大量数据需要进行拷贝的情况下,并且还需要删除临时的存储对象

类似于“夺舍”

右值引用

右值

在讲右值引用之前我们先来介绍一下什么是右值
重点关注红线
1

举例:

int getNumber() {
    return 100;  // 返回值是右值
}

string getName() {
    return "Alice";  // 返回值是右值
}

int main() {
    // 左值例子
    int x = 5;           // x是左值
    int y = x;           // x是左值(但这里被当作右值使用)
    string name = "Bob"; // name是左值
    int arr[5] = {0};    // arr是左值
    
    // 右值例子  
    int z = 10;              // 10是右值
    string s = "Hello";      // "Hello"是右值
    int sum = x + y;         // (x+y)的结果是右值
    int num = getNumber();   // getNumber()返回值是右值
    
    // 验证左值可以取地址
    cout << "x的地址: " << &x << endl;
    cout << "name的地址: " << &name << endl;
    cout << "arr的地址: " << &arr << endl;
    
    // 右值不能取地址(下面的代码会编译错误)
    // &10;                    // 错误
    // &"Hello";               // 错误  
    // &(x + y);               // 错误
    // &getNumber();           // 错误
    
    return 0;
}

右值引用

右值引用的格式为
类型名 & &    右值引用变量名 = 右值表达式 类型名\&\& \ \ \ 右值引用变量名 = 右值表达式 类型名&&   右值引用变量名=右值表达式
举例:

int&& rref = 5 + 3; 

注意:这里的右值引用变量名是个左值

另外右值引用的定义主要是为了区分左值引用
对于左值引用其格式为
类型名 & &    左值引用变量名 = 左值变量 类型名\&\& \ \ \ 左值引用变量名 = 左值变量 类型名&&   左值引用变量名=左值变量
左值引用也就是俗称的引用,其本质是给变量取别名,所以左值变量得是一个变量,而不能是表达式。(具体区分见表格)

移动构造函数

调用时机:使用左值初始化对象时使用复制构造函数,使用右值初始化对象时使用移动构造函数
格式:
类名 : : 类名 ( 类名 & &   对象名 ) 类名::类名(类名\&\& \ \ 对象名) 类名::类名(类名&&  对象名)

委托构造函数

允许一个构造函数调用同一个类中的另一个构造函数,从而避免代码重复。

class 类名 { public: // 主构造函数 类名(参数列表1) : 初始化列表 {} // 委托构造函数 类名(参数列表2) : 类名(构造函数参数-需参考参数列表1) {} 类名(参数列表3) : 类名(构造函数参数-需参考参数列表1) {} }; \begin{aligned} &\text{class 类名 \{} \\ &\quad \text{public:} \\ &\quad \quad \text{// 主构造函数} \\ &\quad \quad \text{类名(参数列表1) : 初始化列表 \{\}} \\ &\quad \quad \text{// 委托构造函数} \\ &\quad \quad \text{类名(参数列表2) : 类名(构造函数参数-需参考参数列表1) \{\}} \\ &\quad \quad \text{类名(参数列表3) : 类名(构造函数参数-需参考参数列表1) \{\}} \\ &\text{\};} \\ \end{aligned} class 类名 {public:// 主构造函数类名(参数列表1) : 初始化列表 {}// 委托构造函数类名(参数列表2) : 类名(构造函数参数-需参考参数列表1) {}类名(参数列表3) : 类名(构造函数参数-需参考参数列表1) {}};

注意:所有委托构造函数完成初始化之后,先执行主构造函数的函数体,再执行自身的函数体。

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

class Rectangle {
private:
    double width;
    double height;
    string color;
    vector<string> tags;
    
public:
    // 主构造函数 - 完成所有初始化
    Rectangle(double w, double h, const string& c, const vector<string>& t)
        : width(w), height(h), color(c), tags(t) {
        cout << "创建矩形: " << w << "x" << h << endl;
    }
    
    // 委托构造函数 - 提供默认颜色和空标签
    Rectangle(double w, double h) 
        : Rectangle(w, h, "黑色", {}) {
        cout << "使用默认颜色" << endl;
    }
    
    // 委托构造函数 - 创建正方形
    Rectangle(double side) 
        : Rectangle(side, side, "蓝色", {"正方形"}) {
        cout << "创建正方形" << endl;
    }
    
    // 委托构造函数 - 默认矩形
    Rectangle() : Rectangle(1.0, 1.0) {
        cout << "创建默认矩形" << endl;
    }
    
  
};


继承构造函数

class 派生类 : 访问说明符 基类 { public: using 基类::基类; // 继承基类的所有构造函数 }; \begin{aligned} &\text{class 派生类 : 访问说明符 基类 \{} \\ &\quad \text{public:} \\ &\quad \quad \text{using 基类::基类; // 继承基类的所有构造函数} \\ &\text{\};} \\ \end{aligned} class 派生类 : 访问说明符 基类 {public:using 基类::基类; // 继承基类的所有构造函数};

转换构造函数(Converting Constructor)

只有一个参数的构造函数统称为转换构造函数

关键字default、delete、explicit

default

显式要求编译器生成默认的特殊成员函数

class MyClass {
public:
    int value;
    
    // 显式要求编译器生成默认构造函数
    MyClass() = default;
    
    // 显式要求编译器生成默认析构函数
    ~MyClass() = default;
    
    // 显式要求编译器生成默认拷贝构造函数
    MyClass(const MyClass&) = default;
    
    // 显式要求编译器生成默认移动构造函数
    MyClass(MyClass&&) = default;
    

};

delete

显式删除特殊成员函数,禁止某些操作

class NonCopyable {
public:
    int value;
    
    NonCopyable(int v) : value(v) {}
    
    // 删除拷贝构造函数 - 禁止拷贝
    NonCopyable(const NonCopyable&) = delete;

};

explicit

防止构造函数的隐式类型转换。

class MyClass {
public:
    // 未使用 explicit - 允许隐式转换
    // MyClass(int x) { }
    
    // 使用 explicit - 禁止隐式转换
    explicit MyClass(double y) { }
};
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐