【问题标题】:c++ copy constructor signature : does it matterc ++复制构造函数签名:有关系吗
【发布时间】:2012-10-02 10:52:58
【问题描述】:

我当前的实现使用大量具有这种语法的复制构造函数

MyClass::Myclass(Myclass* my_class)

它真的(功能上)不同于

MyClass::MyClass(const MyClass& my_class)

为什么?

有人建议我第一个解决方案不是真正的复制构造函数。然而,做出改变意味着相当多的重构。

谢谢!!!

【问题讨论】:

    标签: c++ copy constants copy-constructor


    【解决方案1】:

    在第一个不是复制构造函数而是转换构造函数的意义上有所不同。它从MyClass* 转换为MyClass

    根据定义,复制构造函数具有以下签名之一:

    MyClass(MyClass& my_class)
    MyClass(const MyClass& my_class)
    //....
    //combination of cv-qualifiers and other arguments that have default values
    

    12.8。复制类对象

    2) 类 X 的非模板构造函数是一个复制构造函数,如果它 第一个参数的类型为 X&、const X&、volatile X& 或 const volatile X&,或者没有其他参数,或者其他所有参数 参数具有默认参数 (8.3.6).113) [ 示例:X::X(const X&) 和 X::X(X&,int=1) 是复制构造函数。

    编辑:您似乎混淆了两者。

    假设你有:

    struct A
    {
       A();
       A(A* other);
       A(const A& other);
    };
    
    A a;     //uses default constructor
    A b(a);  //uses copy constructor
    A c(&a); //uses conversion constructor
    

    它们一起服务于不同的目的。

    【讨论】:

    • 为什么我更喜欢复制构造函数而不是转换构造函数?什么是缺点,陷阱...?
    • @fonjibe 他们是不同的。如果要将指针转换为MyClass,则不需要复制构造函数。
    • 不明白为什么它有不同的用途:我总是可以实现复制构造函数,有一个 *MyClass myc_ptr 并通过 MyClass(*myc_ptr) 复制它。你能顺便确认一下这是正确的吗?谢谢!
    • 出于同样的原因,您更喜欢汽车而不是剪刀。他们不做同样的事情。例如。复制 ctor 将在 STL 容器/算法操作中使用,而另一个在那里将无用。
    • @fonjibe 当编译器复制时,它使用一个复制构造函数,并且只有一个复制构造函数。如果你不声明一个,编译器会生成一个,它可能不会做你想要的。
    【解决方案2】:

    第一个版本不是复制构造函数。就那么简单。它只是另一个构造函数。

    Xcopy 构造函数必须具有签名(X &, ...)(X const &, ...)(X volatile &, ...)(X const volatile &, ...),其中除第一个参数之外的所有参数都具有默认值,如果它们是存在(并且不能是模板)。


    也就是说,您应该仔细考虑一下为什么您违反了零规则:大多数精心设计的类不应该有任何用户定义的复制构造函数、复制赋值运算符或析构函数,而是依赖于精心设计的成员。您当前的构造函数采用指针这一事实让我想知道您的代码对于 MyClass x = y; 之类的行为是否正确 - 值得检查。

    【讨论】:

    • 请注意,模板化的 ctor 可能不算作复制 ctor(例如,用于确定是否需要生成复制 ctor),但很可能会选择它们来执行“复制”,即T&& 构造函数让人头疼。 :(
    【解决方案3】:

    某些语言结构需要复制构造函数:

    • 按值传递
    • 按值返回
    • 复制样式初始化(​​尽管在这种情况下通常会省略复制)

    如果语言要求复制,并且您提供了复制构造函数,则可以使用它(当然,假设 cv-qualifications 是可以的)。

    由于您没有提供复制构造函数,因此您将获得编译器生成的复制构造函数。这通过复制每个数据成员来工作,即使这对您的班级来说不是正确的做法。例如,如果您的 not-a-copy-constructor 显式执行任何深层复制或分配资源,那么您需要禁止编译器生成的副本。

    如果编译器生成的副本适用于您的类,那么您的非副本构造函数基本上是无害的。不过,我认为这不是一个特别好的主意:

    void some_function(const MyClass &foo);
    
    MyClass *ptr = new MyClass();
    const MyClass *ptr2 = ptr;
    some_function(ptr); // compiles, but copies *ptr and uses the copy
    MyClass bar(ptr2);  // doesn't compile -- requires pointer-to-non-const
    

    即使假设编译器生成的副本对您的类没有好处,进行必要的更改也不需要进行大量重构。假设您的非构造函数实际上并未修改其参数所指向的对象,因此在修复签名后您拥有:

    MyClass::MyClass(const MyClass* my_class) {
        // maybe treat null pointer specially
        // do stuff here
    }
    

    你需要:

    MyClass::MyClass(const MyClass& my_class) {
        do_stuff(my_class);
    }
    
    MyClass::MyClass(const MyClass* my_class) {
        // maybe treat null pointer specially
        do_stuff(*my_class);
    }
    
    MyClass::do_stuff(const MyClass& my_class) {
        // do stuff here
    }
    

    您还需要将任何初始值设定项列表从旧构造函数复制到新构造函数,并根据my_class 不是新构造函数中的指针这一事实对其进行修改。

    删除旧的构造函数可能需要大量重构,因为您必须编辑任何使用它的代码。不过,您不必删除旧的构造函数来修复默认复制构造函数的任何问题。

    【讨论】:

      【解决方案4】:

      第一个示例不是复制构造函数。这意味着当你提供它时,编译器仍然为你提供了一个默认的复制构造函数,其签名等同于

      MyClass(const MyClass& my_class);
      

      如果你对你的构造函数做了一些特殊的事情,而编译器提供的复制构造函数不遵循这个逻辑,你应该实现一个复制构造函数或者找到一种方法来禁用它。

      【讨论】:

        【解决方案5】:

        为什么要使用复制构造函数而不是转换构造函数?

        如果您将 MyClass 对象提供给一些期望您的复制构造函数有效的代码,则可能会出现问题。

        这就是 STL 容器的情况。例如,如果您使用std::vector<MyClass>,您必须知道向量可以使用其复制构造函数来移动元素以进行重新分配。

        编译器提供的默认构造函数将执行浅拷贝,调用每个属性的拷贝构造函数,为指针等基类型制作简单的拷贝。如果你想要某种形式的深拷贝,你必须正确重写MyClass的拷贝构造函数

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-06-26
          • 1970-01-01
          • 2016-04-08
          • 2013-12-19
          • 1970-01-01
          • 2011-11-28
          • 2016-04-17
          相关资源
          最近更新 更多