我有这个问题,有一个函数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
,然后a
中vec
的副本将是一个悬空指针,对吧?由于这个问题,我希望以这种方式实现ClassA
的复制,
ClassA::ClassA(ClassA &ra) : _ptr(0) //take non-const reference as parameter
{
std::swap(_ptr, a._ptr);
}
我的实施是否正常?或者任何其他方式可以帮助完成这项工作?
答案 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
创建对象并将指针存储到向量中的对象时出现这些问题。