什么时候我们必须使用复制构造函数? 我知道C++编译器为类创建了一个复制构造函数。在哪种情况下,我们必须编写一个用户定义的复制构造函数?你能举几个例子吗
编译器生成的复制构造函数执行成员复制。有时这是不够的。例如:什么时候我们必须使用复制构造函数? 我知道C++编译器为类创建了一个复制构造函数。在哪种情况下,我们必须编写一个用户定义的复制构造函数?你能举几个例子吗,c++,copy-constructor,C++,Copy Constructor,编译器生成的复制构造函数执行成员复制。有时这是不够的。例如: class Class { public: Class( const char* str ); ~Class(); private: char* stored; }; Class::Class( const char* str ) { stored = new char[srtlen( str ) + 1 ]; strcpy( stored, str ); } Class::~Class()
class Class {
public:
Class( const char* str );
~Class();
private:
char* stored;
};
Class::Class( const char* str )
{
stored = new char[srtlen( str ) + 1 ];
strcpy( stored, str );
}
Class::~Class()
{
delete[] stored;
}
在这种情况下,以成员方式复制存储的成员不会复制缓冲区(只复制指针),因此共享缓冲区的第一个要销毁的副本将成功调用delete[]
,第二个将运行未定义的行为。您需要深度复制复制构造函数(以及赋值运算符)
如果您有一个动态分配内容的类。例如,您将一本书的标题存储为char*,并将标题设置为new,copy将不起作用
您必须编写一个复制构造函数,该构造函数执行title=new char[length+1]
,然后执行strcpy(title,titleIn)
。复制构造函数只进行“浅层”复制。当对象按值传递、按值返回或显式复制时,将调用复制构造函数。如果没有复制构造函数,C++创建一个默认拷贝构造器,它创建浅拷贝。如果对象没有指向动态分配内存的指针,那么浅拷贝就可以了。我有点恼火,因为没有引用五个规则
这条规则非常简单:
五法则:
无论何时编写析构函数、复制构造函数、复制赋值运算符、移动构造函数或移动赋值运算符中的任何一个,都可能需要编写其他四个
但是,您应该遵循一个更一般的指导原则,它源于编写异常安全代码的需要:
每个资源都应该由专用对象管理
在这里,@sharptooth
的代码仍然(大部分)很好,但是如果他向他的类添加第二个属性,则不会。考虑下面的类:
class Erroneous
{
public:
Erroneous();
// ... others
private:
Foo* mFoo;
Bar* mBar;
};
Erroneous::Erroneous(): mFoo(new Foo()), mBar(new Bar()) {}
如果newbar
抛出,会发生什么情况?如何删除mFoo
指向的对象?有一些解决方案(功能级别的try/catch…),但它们无法扩展
处理这种情况的正确方法是使用适当的类而不是原始指针
class Righteous
{
public:
private:
std::unique_ptr<Foo> mFoo;
std::unique_ptr<Bar> mBar;
};
我不知道你的情况,但我觉得我的更容易;) 禁用copy-ctor和operator=通常是个好主意,除非类特别需要它。这可以防止低效率,例如在预期引用时按值传递参数。此外,编译器生成的方法可能无效。我可以回忆我的实践,并考虑以下情况,即必须显式声明/定义复制构造函数。我将这些案件分为两类
- 正确性/语义-如果不提供用户定义的复制构造函数,则使用该类型的程序可能无法编译,或可能无法正常工作
- 优化-为编译器生成的复制构造函数提供了一个很好的替代方案,可以使程序更快
正确性/语义
我在本节中介绍了声明/定义复制构造函数对于使用该类型的程序的正确操作是必要的情况
阅读本节之后,您将了解允许编译器自己生成复制构造函数的几个陷阱。因此,正如在his中所指出的,关闭新类的可复制性并在以后真正需要时故意启用它总是安全的
如何在C++03中使类不可复制
声明一个私有复制构造函数,而不为其提供实现(这样,即使该类型的对象是在类自己的作用域中复制的,或者是由它的朋友复制的,构建也会在链接阶段失败)
如何在C++11或更新版本中使类不可复制
用结尾处的=delete
声明复制构造函数
浅拷贝与深拷贝
这是最容易理解的案例,实际上是其他答案中提到的唯一案例。我做得很好。我只想补充一点,应该由对象独占的深度复制资源可以应用于任何类型的资源,动态分配内存只是其中的一种。如果需要,深度复制对象也可能需要
- 复制磁盘上的临时文件
- 打开单独的网络连接
- 创建单独的工作线程
- 分配单独的OpenGL帧缓冲区
- 等
自注册对象
考虑一个类,其中所有对象——无论它们是如何构造的——都必须以某种方式注册。一些例子:
- 最简单的示例:维护当前现有对象的总数。对象注册只是增加静态计数器
- 一个更复杂的例子是有一个单例注册表,其中存储对该类型的所有现有对象的引用(以便可以向所有对象发送通知)
- 引用计数的智能指针可以看作是这类指针中的一个特例:新指针将自己“注册”到共享资源中,而不是在全局注册表中
这种自注册操作必须由该类型的任何构造函数执行,复制构造函数也不例外
具有内部交叉引用的对象
一些对象可能具有非平凡的内部结构,在其不同的子对象之间具有直接的交叉引用(事实上,仅一个这样的内部交叉引用就足以触发这种情况)。编译器提供的复制构造函数将打破内部对象内部关联,将它们转换为对象间关联
例如:
struct MarriedMan;
struct MarriedWoman;
struct MarriedMan {
// ...
MarriedWoman* wife; // association
};
struct MarriedWoman {
// ...
MarriedMan* husband; // association
};
struct MarriedCouple {
MarriedWoman wife; // aggregation
MarriedMan husband; // aggregation
MarriedCouple() {
wife.husband = &husband;
husband.wife = &wife;
}
};
MarriedCouple couple1; // couple1.wife and couple1.husband are spouses
MarriedCouple couple2(couple1);
// Are couple2.wife and couple2.husband indeed spouses?
// Why does couple2.wife say that she is married to couple1.husband?
// Why does couple2.husband say that he is married to couple1.wife?
仅允许满足特定条件的对象
class Class
{
public:
Class(char const* str): mData(str) {}
private:
std::string mData;
};
struct MarriedMan;
struct MarriedWoman;
struct MarriedMan {
// ...
MarriedWoman* wife; // association
};
struct MarriedWoman {
// ...
MarriedMan* husband; // association
};
struct MarriedCouple {
MarriedWoman wife; // aggregation
MarriedMan husband; // aggregation
MarriedCouple() {
wife.husband = &husband;
husband.wife = &wife;
}
};
MarriedCouple couple1; // couple1.wife and couple1.husband are spouses
MarriedCouple couple2(couple1);
// Are couple2.wife and couple2.husband indeed spouses?
// Why does couple2.wife say that she is married to couple1.husband?
// Why does couple2.husband say that he is married to couple1.wife?
class base{
int a, *p;
public:
base(){
p = new int;
}
void SetData(int, int);
void ShowData();
base(const base& old_ref){
//No coding present.
}
};
void base :: ShowData(){
cout<<this->a<<" "<<*(this->p)<<endl;
}
void base :: SetData(int a, int b){
this->a = a;
*(this->p) = b;
}
int main(void)
{
base b1;
b1.SetData(2, 3);
b1.ShowData();
base b2 = b1; //!! Copy constructor called.
b2.ShowData();
return 0;
}
Output:
2 3 //b1.ShowData();
1996774332 1205913761 //b2.ShowData();