一个复杂的类,定义其对象时可能有多种初始化的方法,不仅他们之间很容易搞混,还容易和先定义再赋值的一些情况混淆,本节我们详细列举常遇到的初始化方式,用于区分学习。

几种常用初始化方式:

先强调一下:任何类一定要自主设置构造函数,一定不能使用系统自带的默认构造函数,因为成员变量会被赋危险不可知的值。

可行:
1.无参初始化 - 调用无参构造函数(内部用初始化列表处理),这是自行设置默认构造函数。
2.直接初始化 - 使用构造函数参数
3.拷贝初始化 - 使用等号和已有对象
4.列表初始化 - C++11引入的花括号语法
5.值初始化 - 使用空花括号

#include <iostream>
#include <string>
#include <vector>
#include <initializer_list>

class ComplexClass {
private:
    int id;
    std::string name;
    std::vector<int> data;
    double* values;
    size_t size;

public:
    // 无参构造函数
    ComplexClass() : id(0), name("Default"), values(nullptr), size(0) {
        std::cout << "Default constructor called" << std::endl;
    }

    // 参数化构造函数
    ComplexClass(int id, const std::string& name, std::initializer_list<int> list) 
        : id(id), name(name), data(list), size(list.size()) {
        values = new double[size];
        for (size_t i = 0; i < size; ++i) {
            values[i] = i * 1.1;
        }
        std::cout << "Parameterized constructor called" << std::endl;
    }

    // 拷贝构造函数
    ComplexClass(const ComplexClass& other) 
        : id(other.id), name(other.name), data(other.data), size(other.size) {
        values = new double[size];
        for (size_t i = 0; i < size; ++i) {
            values[i] = other.values[i];
        }
        std::cout << "Copy constructor called" << std::endl;
    }

    // 拷贝赋值运算符
    ComplexClass& operator=(const ComplexClass& other) {
        if (this != &other) {
            delete[] values;
            
            id = other.id;
            name = other.name;
            data = other.data;
            size = other.size;
            
            values = new double[size];
            for (size_t i = 0; i < size; ++i) {
                values[i] = other.values[i];
            }
        }
        std::cout << "Copy assignment operator called" << std::endl;
        return *this;
    }

    // 析构函数
    ~ComplexClass() {
        delete[] values;
        std::cout << "Destructor called for ID: " << id << std::endl;
    }

    // 显示对象内容
    void display() const {
        std::cout << "ID: " << id << ", Name: " << name << std::endl;
        std::cout << "Data: ";
        for (int num : data) {
            std::cout << num << " ";
        }
        std::cout << std::endl;
        
        std::cout << "Values: ";
        for (size_t i = 0; i < size; ++i) {
            std::cout << values[i] << " ";
        }
        std::cout << std::endl << std::endl;
    }

    // 设置ID
    void setId(int newId) {
        id = newId;
    }

    // 设置名称
    void setName(const std::string& newName) {
        name = newName;
    }
};

int main() {
    std::cout << "=== C++ 对象初始化与赋值示例 ===" << std::endl << std::endl;
    
    // 1. 无参初始化
    std::cout << "1. 无参初始化:" << std::endl;
    ComplexClass obj1;
    obj1.display();
    
    // 2. 直接初始化(使用构造函数)
    std::cout << "2. 直接初始化:" << std::endl;
    ComplexClass obj2(101, "DirectInit", {1, 2, 3, 4, 5});
    obj2.display();
    
    // 3. 拷贝初始化
    std::cout << "3. 拷贝初始化:" << std::endl;
    ComplexClass obj3 = obj2;
    obj3.setId(102);
    obj3.setName("CopyInit");
    obj3.display();
    
    // 4. 列表初始化
    std::cout << "4. 列表初始化:" << std::endl;
    ComplexClass obj4{103, "ListInit", {6, 7, 8, 9}};
    obj4.display();
    
    // 5. 值初始化
    std::cout << "5. 值初始化:" << std::endl;
    ComplexClass obj5{};
    obj5.setId(104);
    obj5.setName("ValueInit");
    obj5.display();
    
    std::cout << "=== 程序结束,自动调用析构函数 ===" << std::endl;
    return 0;
}

区分初始化和赋值

一般来说,等号在声明中通常表示初始化,而在语句中(对象已存在)表示赋值。

#include <iostream>
#include <string>

class MyClass {
private:
    int value;
    std::string name;
    
public:
    // 默认构造函数
    MyClass() : value(0), name("Default") {
        std::cout << "默认构造函数" << std::endl;
    }
    
    // 参数化构造函数
    MyClass(int v, const std::string& n) : value(v), name(n) {
        std::cout << "参数化构造函数: " << name << std::endl;
    }
    
    // 拷贝构造函数
    MyClass(const MyClass& other) : value(other.value), name(other.name + "_copy") {
        std::cout << "拷贝构造函数: " << name << std::endl;
    }
    
    // 拷贝赋值运算符
    MyClass& operator=(const MyClass& other) {
        value = other.value;
        name = other.name + "_assigned";
        std::cout << "拷贝赋值运算符: " << name << std::endl;
        return *this;
    }
    
    void display() const {
        std::cout << "Value: " << value << ", Name: " << name << std::endl;
    }
};

int main() {
    std::cout << "=== 等于号作为初始化 ===" << std::endl;
    
    // 情况1: 声明时的等于号是初始化(拷贝初始化)
    std::cout << "\n1. 拷贝初始化:" << std::endl;
    MyClass obj1(10, "Original");
    MyClass obj2 = obj1;  // 这是初始化!调用拷贝构造函数
    obj2.display();
    
    // 情况2: 带参数的初始化
    std::cout << "\n2. 带参数的初始化:" << std::endl;
    MyClass obj3 = MyClass(20, "Temporary");  // 初始化,可能被优化
    obj3.display();
    
    // 情况3: 列表初始化(C++11)
    std::cout << "\n3. 列表初始化:" << std::endl;
    MyClass obj4 = {30, "FromList"};  // 初始化
    obj4.display();
    
    std::cout << "\n=== 等于号作为赋值 ===" << std::endl;
    
    // 情况4: 已存在对象的等于号是赋值
    std::cout << "\n4. 对象赋值:" << std::endl;
    MyClass obj5(40, "FirstValue");
    MyClass obj6(50, "SecondValue");
    obj5 = obj6;  // 这是赋值!调用拷贝赋值运算符
    obj5.display();
  
    return 0;
}

封闭类和派生类的初始化

如果对内部类和基类自定义了构造函数

注意事项


1.性能差异:初始化通常比"先默认构造再赋值"更高效
2.const成员和引用成员:必须在初始化列表中初始化,不能在构造函数体中赋值
3.explicit构造函数:影响等于号初始化的行为

class ExplicitExample {
    int x;
public:
    explicit ExplicitExample(int val) : x(val) {}
};

void explicitExample() {
    // ExplicitExample obj = 10;  // 错误:explicit构造函数不能用于拷贝初始化
    ExplicitExample obj(10);      // 正确:直接初始化
    ExplicitExample obj2 = ExplicitExample(10); // 正确:显式构造
}

优先使用初始化而不是先构造后赋值
对于类类型成员,使用成员初始化列表
注意区分声明时的等于号(初始化)和语句中的等于号(赋值)

Logo

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

更多推荐