You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

拷贝构造函数与移动构造函数的区别及相关代码疑问咨询

Hey there! Let's unpack your questions one by one—first clarifying the difference between copy and move constructors with examples, then diving into why your code behaves the way it does.

Copy Constructor vs. Move Constructor: Key Differences & Simple Examples

Let's start with the core purpose and behavior of each:

Copy Constructor

  • What it does: Creates a new object that's an exact, independent copy of an existing object. It works with lvalues (objects that have a persistent identity, like a named variable).
  • Signature: Typically takes a const lvalue reference: ABC(const ABC& other) (the const is crucial for flexibility—we'll come back to this!).
  • Cost: Copies all resources (like dynamic memory) from the original object, which can be expensive for large objects.

Move Constructor

  • What it does: Transfers ownership of resources from a temporary object (rvalue) to a new object. Temporary objects are short-lived (like the return value of a function) and are about to be destroyed anyway.
  • Signature: Takes an rvalue reference: ABC(ABC&& other) (the && denotes an rvalue reference).
  • Cost: No resource copying—just "takes over" the original object's pointers/resources, then nulls out the temporary to avoid double destruction. This is vastly more efficient.

Example Code

Here's a concrete example using a class with dynamic memory to show the difference:

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

class MyString {
private:
    char* data;
    size_t len;

public:
    // Basic constructor
    MyString(const char* str = "") {
        len = strlen(str);
        data = new char[len + 1];
        strcpy(data, str);
        cout << "Basic Constructor called\n";
    }

    // Copy Constructor
    MyString(const MyString& other) {
        len = other.len;
        data = new char[len + 1]; // Allocate new memory
        strcpy(data, other.data); // Copy actual data
        cout << "Copy Constructor called\n";
    }

    // Move Constructor
    MyString(MyString&& other) noexcept {
        // Take over the existing resources
        len = other.len;
        data = other.data;
        // Null out the temporary object to prevent double free
        other.len = 0;
        other.data = nullptr;
        cout << "Move Constructor called\n";
    }

    // Destructor
    ~MyString() {
        if (data) {
            delete[] data;
            cout << "Destructor called\n";
        }
    }
};

int main() {
    // Use copy constructor: copy from a named lvalue
    MyString str1("Hello");
    MyString str2 = str1;

    // Use move constructor: take resources from a temporary rvalue
    MyString str3 = MyString("World");
    return 0;
}

Sample Output:

Basic Constructor called
Copy Constructor called
Basic Constructor called
Move Constructor called
Destructor called
Destructor called
Destructor called

Your Code Behavior Explained

Let's break down why ABC obj1 = fun123(); uses the move constructor (or skips it entirely with NRVO):

First, let's note a critical detail in your code: your copy constructor has the signature ABC(ABC& obj) (no const). This means it cannot accept rvalues (like the temporary object returned by fun123()), since rvalues can't bind to non-const lvalue references.

Now, let's cover two scenarios:

1. When NRVO is Enabled (Default for Most Compilers)

NRVO stands for Named Return Value Optimization. It's a compiler optimization that eliminates the need for copy/move operations when returning a named object from a function. Instead of constructing the object in fun123() and then copying/moving it to obj1, the compiler directly constructs the object in main()'s obj1 memory space.

In this case, you'll only see:

Constructor
Destructor

No copy or move constructor is called—they're optimized away entirely.

2. When NRVO is Disabled (or Not Applied)

If you disable optimization (e.g., with g++ -fno-elide-constructors), here's what happens:

  • fun123() constructs obj (outputs "Constructor").
  • fun123() returns a temporary copy of obj—but since your copy constructor can't accept the rvalue temporary, the compiler looks for a move constructor.
  • If your move constructor is uncommented, it's called (outputs "Move Constructor") to transfer the temporary's resources to obj1.
  • The temporary object is destroyed (outputs "Destructor"), then obj1 is destroyed at the end of main() (another "Destructor").

Why You Expected Copy Constructor

You probably assumed the temporary would use the copy constructor, but your non-const copy constructor signature blocks that. If you modify the copy constructor to ABC(const ABC& obj), then when NRVO is disabled, the copy constructor would be called instead—but the move constructor is still preferred for rvalues if it exists, since it's more efficient.

Modified Code to Test

Try this version with a const copy constructor to see the difference:

#include <iostream>
using namespace std;
class ABC {
public:
 const char *a;
 ABC() { cout<<"Constructor"<<endl; }
 ABC(const char *ptr) { cout<<"Constructor"<<endl; }
 ABC(const ABC &obj) { cout<<"copy constructor"<<endl;} // Added const
 ABC(ABC&& obj) { cout<<"Move constructor"<<endl; }
 ~ABC() { cout<<"Destructor"<<endl; }
};
ABC fun123() {
 ABC obj;
 return obj;
}
int main() {
 ABC obj1=fun123();
 return 0;
}

Compile with g++ -fno-elide-constructors test.cpp and run—you'll see the move constructor is called instead of the copy constructor, since it's the better match for rvalues.


内容的提问来源于stack exchange,提问作者sunshilong369

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 21:12:36