Skip to content

Instantly share code, notes, and snippets.

@pengfeiw
Last active May 8, 2025 10:52
Show Gist options
  • Select an option

  • Save pengfeiw/6c5bbe73ecbc5b52a6f09b6fae2e24ee to your computer and use it in GitHub Desktop.

Select an option

Save pengfeiw/6c5bbe73ecbc5b52a6f09b6fae2e24ee to your computer and use it in GitHub Desktop.
Copy Control In C++:copy constructor、assignment operator

拷贝构造函数 (copy constructor)

默认生成的拷贝构造函数

当一个类没有显示定义一个拷贝构造函数,编译器会自动添加一个默认的拷贝构造函数。

#include <iostream>
using namespace std;

// Create a demo class
class A {
public:
    int x;
};

int main() {
  
  	// Creating an a1 object
    A a1;
    a1.x = 10;
    cout << "a1's x = " << a1.x << endl;

    // Creating another object using a1
  	// Copy Constructor Calling
    A a2(a1);
    cout << "a2's x = " << a2.x;

    return 0;
}

默认生成的拷贝构造函数,会按照成员变量的声明顺序,依次进行赋值(member-wise initialization)。

自定义拷贝构造函数

也可以自己主动为类定义构造函数,格式如下。

className (const ClassName &obj) {
    // Body of copy constructor
}

const 关键字是可选的,因为构造函数里面不会去更改 obj,所以添加 const 修饰符。

Example:

#include <iostream>
using namespace std;

class A {
public:
    int x;
    A(){};
  
  	// Copy Constructor definition
    A (A& obj) {
        x = obj.x;
      	cout << "Copy constructor "
      	        "called" << endl;
    }
};

int main() {
  
  	// Creating an object of
  	// class A
    A obj1;
    obj1.x = 10;
    cout << "obj1's x = " << obj1.x << endl;

    // Creating another object by
    // copying already created object
    A obj2(obj1);
    cout << "obj2's x = " << obj2.x;
    return 0;
}

既然,编译器会自动生成默认拷贝构造函数,为什么需要自定义拷贝构造函数? 默认生成的拷贝构造函数,仅进行了浅拷贝,如果一个类有一个指针或者动态分布的资源等,默认生成的构造函数仅拷贝了指针的地址,没有对指针指向的资源进行赋值,如果原始类对象析构后,资源也会被销毁,此时就会造成野指针问题,指针指向的内存资源被删除了。

下面是一个例子,需要显示声明拷贝构造函数。

#include <bits/stdc++.h>
using namespace std;

class String {
private:
    char* s;
    int size;

public:
    String(const char* str){
        size = strlen(str);
        s = new char[size + 1];
        strcpy(s, str);
    }
    ~String() { delete[] s; }
  
  	 // Copy constructor
    String(const String& old_str){
        size = old_str.size;
        s = new char[size + 1];
        strcpy(s, old_str.s);
    }
    void print() {
        cout << s << endl;
    }
    
    void change(const char* str){
        delete[] s;
        size = strlen(str);
        s = new char[size + 1];
        strcpy(s, str);
    }
};

int main() {
    String str1("GeeksQuiz");
  
  	// Create str2 from str1
    String str2 = str1;
    str1.print();
    str2.print();

    // Update the str2 object
    str2.change("GeeksforGeeks");

    str1.print();
    str2.print();
    return 0;
}

赋值操作符 (assignment operator)

默认生成的赋值操作符

同拷贝构造函数一样,如果没有显示的定义赋值操作符,编译器也会自动生成赋值操作符,默认生成的赋值操作符和默认生成的拷贝构造函数存在同样的问题,都是对成员变量进行浅拷贝,当类成员指向动态分配资源时,原始对象析构时,资源会被销毁。此时需要自定义赋值操作符。

自定义赋值操作符

格式:

classname operator=(const classname& c) {

}

因为赋值时并不会修改源对象,所以使用 const 修饰符。

下面是一个存在动态资源的类的赋值操作符实现方式,对其进行了深拷贝。

#include <bits/stdc++.h>
using namespace std;

// Dummy class
class GfG {
public:
    int* arr;
    int _size;
    GfG(int size = 3) {
        arr = new int[size];
        _size = size;
    }
    
    // Overloaded assignment operator
    GfG& operator=(const GfG& c) {
        
        // self assignment check
        if (this != &c) {
            
            // Allocate new block
            int* temp = arr;
            arr = new(nothrow) int[c._size];
            
            // If allocation fails
            if (arr == nullptr) {
                arr = temp;
            }
            else {
                
                // Deallocate current block
                delete [] temp;
                
                // Copy data
                _size = c._size;
                memmove(arr, c.arr, _size * sizeof(int));
            }
        }
        return *this;
    }
    
    // Deleting dynamic resources
    ~GfG() {
        delete arr;
    }
};

int main() {
    GfG c1,c2;
    
    // Initialize c2.arr with some values
    for (int i = 0; i < c1._size; i++) {
        c2.arr[i] = i + 10;
    }
    
    // Copying c2 object to c1
    c1 = c2;
    
    // Print the pointers to the allocated memory
    cout << c1.arr << endl;
    cout << c2.arr;
    
    return 0;
}

什么时候调用拷贝构造函数,什么时候调用赋值操作符呢?

可以看下面的例子:

MyClass t1, t2;
MyClass t3 = t1;  // ----> (1)
t2 = t1;          // -----> (2)

因为(1)中创建了一个新的 t3 对象,所以调用拷贝构造函数。而(2)中 t2 是已经存在的对象,这里仅仅是将 t1 赋值给 t2,并不是一个初始化的过程,所以调用的是赋值运算符。

移动构造函数(move constructor)和移动赋值操作符(move assignment operator)

移动构造函数比较特殊,和拷贝构造函数目的相同,都是从一个已有的对象创建一个新的对象,不同的是它不是通过拷贝类成员实现的,而是使新对象指向内存中已经存在的对象,类似移动资源。

移动构造函数和移动赋值操作符接受的参数是一个右值引用对象。

ClassName(ClassName&& obj) {
    data = obj.data;
    // Nulling out the pointer to the temporary data
    obj.data = nullptr;
}

ClassName& operator=(ClassName&& other) {
    if (this != &other) {
        data = other.data;
        other.data = nullptr;
    }
    return *this;
}

这里进行了一个资源转移的过程,将 obj.data 的资源转移到新对象中。

// C++ Program to illustrate how to use the move constructor
#include <iostream>

using namespace std;

class Simple {
private:
    int* data;

public:
    // Constructor
    Simple(int value) : data(new int(value))
    {
        cout << "Constructor called, data = " << *data
             << endl;
    }

    // Destructor
    ~Simple()
    {
        delete data;
        cout << "Destructor called" << endl;
    }

    // Move constructor
    Simple(Simple&& other)
        : data(other.data)
    {
        // nullify the other object resource
        other.data = nullptr;
        cout << "Move constructor called" << endl;
    }

    // Move assignment operator
    Simple& operator=(Simple&& other)
    {
        if (this != &other) {
            delete data; // Free existing resource
            data = other.data; // Transfer ownership
            other.data = nullptr; // Nullify source
            cout << "Move assignment called" << endl;
        }
        return *this;
    }

    // Disable copy constructor and copy assignment operator
    Simple(const Simple&) = delete;
    Simple& operator=(const Simple&) = delete;

    // Function to print the value
    void print()
    {
        if (data) {
            cout << "Data: " << *data << endl;
        }
        else {
            cout << "Data is null" << endl;
        }
    }
};

int main()
{
    // Create a Simple object with value 42
    Simple obj1(42);
    obj1.print();

    // Move obj1 to obj2 using move constructor
    Simple obj2 = move(obj1);
    // Print obj2's data
    obj2.print();
    // Print obj1's data after move
    obj1.print();

    // Create another Simple object with value 84
    Simple obj3(84);
    // Move obj2 to obj3 using move assignment
    obj3 = move(obj2);
    obj3.print();
    obj2.print();

    return 0;
}

std::move 方法将一个左值转换成右值引用,从而可以调用移动构造函数和移动赋值操作符。

移动构造函数因为避免了资源的拷贝,所以性能会更好。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment