复制构造函数可以采用非const参数吗?

时间:2012-07-27 08:13:19

标签: c++

我有这个问题,有一个函数foo()如下,

vector<ClassA> vec;

void foo()
{
    ClassA a;   //inside foo, a ClassA object will be created
    a._ptr = new char[10];

    vec.push_back(a);   //and this newly created ClassA object should be put into vec for later use
}

AFAIK,vec将调用ClassA的copy-ctor来制作新创建的对象a的副本,这就是问题所在。如果我按照通常的方式定义ClassA的复制文件,

ClassA::ClassA(const ClassA &ra) : _ptr(0)
{
    _ptr = ra._ptr;
}

然后对象a及其副本(由vec创建)将指向同一区域的指针_ptr,当foo完成时,a将调用析构函数发布_ptr,然后avec的副本将是一个悬空指针,对吧?由于这个问题,我希望以这种方式实现ClassA的复制,

ClassA::ClassA(ClassA &ra) : _ptr(0) //take non-const reference as parameter
{
    std::swap(_ptr, a._ptr);
}

我的实施是否正常?或者任何其他方式可以帮助完成这项工作?

5 个答案:

答案 0 :(得分:7)

回答您的名义问题:是的,类T的任何构造函数都有一个类型为T &T const &的强制参数(它可能还有其他默认参数)复制构造函数。在C ++ 11中,还有一个 move 构造函数,它需要一个T &&类型的参数。

拥有一个非常量的复制构造函数实际上会改变参数,这会给你的类带来非常不寻常的语义(通常是“传递语义”)并且应该被广泛记录;它还可以防止你复制一些不变的东西(显然)。旧的std::auto_ptr<T>就是这样做的。

如果可能的话,新的C ++ 11样式的可变rvalue引用和移动构造函数为原始对象中不再需要的“移动”资源的问题提供了更好的解决方案。这是因为右值引用是对可变对象的引用,但它只能绑定到“安全”表达式,例如临时或已显式转换的事物(通过std::move)和因此标记为一次性。

答案 1 :(得分:3)

C ++ 11为此目的引入了移动构造函数:

ClassA::ClassA(ClassA&& ra)
: _ptr(ra._ptr)
{
    ra._ptr = nullptr;
}

或者,您可以将_ptr声明为共享指针:

std::shared_ptr<char[]> _ptr;

然后默认的denerated copy构造函数就可以了。

答案 2 :(得分:2)

您不应该复制指针,您应该复制指针指向的上下文。您还应该通过告诉它需要多少元素来初始化类,而不是通过访问公共指针来分配它。

由于您要复制对象而不是移动它,因此您应该在复制时在新对象中分配资源。

class A {

        int* p_;
        int size_;
public:

        A(int size) 
        : p_(new int[size]()), 
        size_(size) {
        }

        A(const A &a) 
        : p_(new int[a.size_]),
         size_(a.size_) {               
                std::copy(a.p_, a.p_ + a.size_, p_);
        }

        ...

};


int main () {
        A a(10);
        vec.push_back(a);
}

但是,如果您知道要复制的对象在复制后未使用,则可以移动它的资源。

答案 3 :(得分:1)

您的实现问题是您将无法将临时对象作为此copy-ctor的参数传递(临时对象始终为const)。就像已经提到的那样,最好的解决方案是转移到c ++ 11并使用移动语义。如果不可能shared_array可以替代。

答案 4 :(得分:0)

补充评论:

避免使用new创建对象并将指针存储到向量中的对象时出现这些问题。