C++

C++ 知识量:19 - 82 - 316

13.1 拷贝、赋值与销毁><

拷贝构造函数- 13.1.1 -

拷贝构造函数是一种特殊的构造函数,用于创建一个新对象,该对象的内容是另一个已存在的对象的复制。在C++中,拷贝构造函数通常具有以下形式:

class_name (const class_name &old_obj);

其中,class_name 是类的名称,&old_obj 是对已存在对象的引用。这个构造函数会接收一个指向已存在对象的引用作为第一个参数,并且通常会接受其他参数,这些参数具有默认值。

下面是一个简单的例子:

class MyClass {  
    int value;  
public:  
    MyClass(const MyClass &other) : value(other.value) {  
        // 拷贝构造函数,复制 other 对象的值到新创建的对象中  
    }  
};

在这个例子中,MyClass类有一个成员变量value。创建MyClass的新对象时,如果使用默认构造函数(即没有提供任何参数),那么每个对象的value成员都会被初始化为0。但是,如果想要一个新的MyClass对象,其value成员与另一个对象的value成员具有相同的值,就可以使用拷贝构造函数。

例如:

MyClass obj1(5); // 使用默认构造函数,value = 5      
MyClass obj2 = obj1; // 使用拷贝构造函数,obj2.value 也为 5

在这个例子中,obj2是通过拷贝构造函数创建的,其value成员的值等于obj1的value成员的值。这就是拷贝构造函数的基本作用:它用于创建新对象,该对象的内容是另一个已存在的对象的复制。

拷贝赋值运算符- 13.1.2 -

拷贝赋值运算符(copy assignment operator)是C++中的一个特殊成员函数,用于控制类对象的赋值操作。它通常具有以下形式:

class_name &operator=(const class_name &rhs);

其中,class_name 是类的名称,&rhs 是对右侧对象的引用,该函数返回一个指向类对象的引用。

拷贝赋值运算符与拷贝构造函数类似,但它们的目的不同。拷贝构造函数用于初始化新对象,而拷贝赋值运算符用于将一个已存在的对象赋值给另一个对象。

当使用拷贝赋值运算符时,左侧对象将通过引用接收右侧对象的值,然后根据左侧对象的类型进行适当的赋值操作。这个过程通常包括释放左侧对象中原有的资源,并使用右侧对象的值来初始化或更新左侧对象的成员变量。

下面是一个简单的例子:

class MyClass {  
    int value;  
public:  
    MyClass(int val) : value(val) {  
        // 构造函数,用于初始化对象  
    }  
    MyClass(const MyClass &other) : value(other.value) {  
        // 拷贝构造函数,用于初始化新对象  
    }  
    MyClass &operator=(const MyClass &rhs) {  
        // 拷贝赋值运算符,用于将右侧对象赋值给左侧对象  
        if (this != &rhs) {  
            value = rhs.value; // 更新左侧对象的值  
        }  
        return *this; // 返回指向左侧对象的引用  
    }  
};

在这个例子中,MyClass类有一个成员变量value。拷贝赋值运算符会检查左侧对象和右侧对象是否是同一个对象,如果不是,就更新左侧对象的value成员变量并返回指向左侧对象的引用。这样就可以实现类对象的赋值操作。

使用=default- 13.1.3 -

在C++中, "=default" 是一个特殊的标记,用于指示编译器生成默认的构造函数、析构函数、拷贝构造函数、拷贝赋值运算符或移动构造函数/移动赋值运算符。

在类中声明了 "=default" 时,编译器会生成一个默认的构造函数、析构函数、拷贝构造函数、拷贝赋值运算符或移动构造函数/移动赋值运算符,这些函数的实现将与常规的构造函数、析构函数、拷贝构造函数、拷贝赋值运算符或移动构造函数/移动赋值运算符相同。

例如,如果在类中声明了一个默认构造函数,但是没有提供实现,那么可以使用 "=default" 来指示编译器生成默认的构造函数实现:

class MyClass {  
public:  
    MyClass() =default; // 指示编译器生成默认构造函数实现  
};

同样地,也可以使用 "=default" 来指示编译器生成析构函数、拷贝构造函数、拷贝赋值运算符或移动构造函数/移动赋值运算符的实现。

需要注意的是, "=default" 只能用于类的成员函数,不能用于全局函数或函数模板。此外,如果类的成员函数有多个默认的构造函数、析构函数、拷贝构造函数、拷贝赋值运算符或移动构造函数/移动赋值运算符,那么只能使用一个 "=default" 来指示编译器生成一个默认的实现。

阻止拷贝- 13.1.4 -

在C++中,如果不希望一个类被复制或赋值,可以通过不定义拷贝构造函数和拷贝赋值运算符来实现。然而,这并不能完全阻止拷贝行为,因为如果类中没有定义这些操作,编译器将自动生成合成的版本。

为了真正阻止拷贝,可以将类的拷贝构造函数和拷贝赋值运算符设置为private,这样用户就不能直接复制类的对象了。另外,还可以将类的拷贝构造函数和拷贝赋值运算符设置为delete,这样编译器也不会生成合成的拷贝控制成员。

例如:

class NonCopyable {  
public:  
    NonCopyable() = default;  
    NonCopyable(const NonCopyable&) = delete; // 删除拷贝构造函数  
    NonCopyable& operator=(const NonCopyable&) = delete; // 删除拷贝赋值运算符  
};

这样,任何试图复制NonCopyable对象的尝试都会导致编译错误。

对于iostream类,它是一个流类,通常用于处理输入/输出操作。为了避免多个对象同时写入或读取相同的IO缓冲区,它的拷贝构造函数和拷贝赋值运算符被定义为private,这样就无法直接复制iostream对象。这也是一种阻止拷贝的策略。