【问题标题】:why are copy constructors needed and what are the cases where they are very helpful? [duplicate]为什么需要复制构造函数?它们在哪些情况下非常有用? [复制]
【发布时间】:2026-01-10 08:00:01
【问题描述】:

为什么我们需要复制构造函数?

我正在学习 C++,但我无法理解对复制构造函数的需求,因为也没有使用复制构造函数,我得到了正确的输出。 我经历了几个例子,但在我看来,拥有复制构​​造函数只是一个好习惯,比如初始化变量。有人可以帮我理解复制构造函数的概念。 任何帮助将不胜感激。 谢谢。

【问题讨论】:

  • 因为简单的值副本(就像 C 所做的那样)对于非值的东西是不够的,例如指针或其他句柄。写下你的向量作为练习。
  • 这能回答你的问题吗? What is The Rule of Three?
  • 您不一定需要复制构造函数。仅当您要复制构造对象时;)。有些类没有
  • 你可以使用Foo(Foo const&) = delete;来省去Foo的拷贝构造函数。
  • 我认为你错过了一个事实,即如果你不提供复制构造函数,编译器会为你生成一个。这适用于 C++ 中的每个 class/struct。编译器生成的复制构造函数对于大多数类来说已经足够了,但是如果你在一个类中管理一些资源(比如使用new 分配一些东西),你需要提供你自己的复制构造函数。

标签: c++ copy-constructor


【解决方案1】:

因为不使用复制构造函数,我们可以得到想要的 输出

这实际上不是真的。您没有编写复制构造函数,但您通过复制构造函数获得所需的输出,您的编译器会为您编写:

如果没有为类类型提供用户定义的复制构造函数 (结构、类或联合),编译器将始终声明一个副本 构造函数作为其类的非显式内联公共成员

有关隐式声明的复制构造函数的更多详细信息,请参阅https://en.cppreference.com/w/cpp/language/copy_constructor


简而言之,复制构造函数是在复制类的实例时调用的函数。编译器会为您编写它,但如果您希望它做一些重要的事情(例如不复制指针而是复制它指向的对象),您可以自己编写它

【讨论】:

    【解决方案2】:

    让我们看下一个例子

    #include <iostream>
    struct A {
        explicit A(int value) : data_(new int[1]) { data_[0] = value; }
        ~A() { delete [] data_; }
    
        int getValue() const { return data_[0]; }
    
      private:
        int *data_ = nullptr;
    };
    
    int main() {
        A a1(1);
        A a2(2);
        a2 = a1;
        std::cout << a2.getValue() << std::endl;
    }
    

    如果你执行它,程序会崩溃。

    这里发生的情况是您将int *data_ 字段从a1 复制到a2,然后a2 分配的内存被泄露。

    现在a1a2 具有相同的指针。他们都会调用析构函数来释放内存。内存将被释放两次,这会导致崩溃。

    提供正确的复制构造函数将解决此问题。

    【讨论】:

    • 无法复制。当我编译、链接和运行该代码时,我得到了2 的输出,并且没有程序崩溃。该行为实际上是未定义的 - 说会崩溃是一种误导,因为崩溃只是未定义行为的一种可能症状。
    • 你的想法是对的,但是a2 = a1; 使用赋值运算符,而不是复制构造函数。 A a2 = a1;将使用复制构造函数。
    【解决方案3】:

    如果您通过复制传递将对象传递给方法,C++ 必须使用复制构造函数为方法上下文生成该类的实例。例如,如果您将实例存储在向量中,当您推回一个对象时,它会间接调用复制构造函数。顺便说一句,对于类,如果没有显式实现,c++ 通常会创建一个默认的复制构造函数,这种情况有时会产生不需要的效果(浅复制)。

    【讨论】:

      【解决方案4】:

      三法则(也称为三巨头法则或三巨头法则)是 C++(C++11 之前)中的一条经验法则,它声称如果一个类定义了以下任何一项,那么它可能应该明确定义所有三个:析构函数。复制构造函数。复制赋值运算符。

      要点:当您在对象构造函数中有动态内存分配(堆)时,复制构造函数是必需的,您还需要将分配的内存复制到新分配的对象。这样你就可以 (Obj1 = Obj2 / Obj1(Obj2) ) 并保证动态内存也将被复制。

      【讨论】:

      • 在 C++11 之后,三规则仍然适用,因为并非所有东西都可以/必须是可移动的
      最近更新 更多