C++拷贝构造函数

拷贝构造函数

对于普通类型的对象来说,它们之间的复制是很简单的,例如:
int a=88;
int b=a;
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。

本文地址:http://www.cnblogs.com/archimedes/p/cpp-copy-constructor.html,转载请注明源地址。

1、对象的定义形式

C++支持两种定义形式:直接初始化和复制初始化,复制初始化采用=符号,直接初始化将初始化式放在圆括号中

用于类类型对象时,直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用拷贝构造函数,复制初始化首先使用指定构造函数创建一个临时对象,然后拷贝构造函数将那个临时对象拷贝到正在创建的对象

string null_book="9-454-45546"; //复制初始化 string dots(10,'.'); //直接初始化 string empty_copy=string(); //复制初始化 string empty_direct; //直接初始化

通常,直接初始化和复制初试化只在低级别优化上存在差异,但是对于不支持复制的类型,或者使用非explicit构造函数的时候,它们就有本质的区别:

ifstream file1("filename"); //ok
ifstream file2="filename"; //error! 复制构造函数是私有的

2、形参与返回值

当形参是非引用类型的时候,将复制实参的值,以非引用类型作为返回值的时候,将返回return语句中值的副本

3、初始化容器元素

复制初始化函数可用于初始化顺序容器中的元素,容器的这种构造方式使用了默认构造函数和拷贝构造函数:

vector<string> svec(5);

4、构造函数与数组元素

如果没有为类类型数组提供元素的初始化形式,则将使用默认构造函数初始化每个元素,然而,如果使用常规的花括号括住的数组初始化列表来提供显示元素的初始化式,则使用复制初始化来初始化每个元素:

A a[]={ string("hdsf"),
 string("weiyrw"),
 string("cxvbc"),
 A()
};

合成的拷贝构造函数

如果没有定义拷贝构造函数,编译器会为我们合成一个,即使我们定义了其他的构造函数,也会合成拷贝构造函数。合成拷贝构造函数的行为是,执行逐个成
员的初始化。编译器将现有对象的每个非static成员,依次复制到正创建的对象,合成拷贝构造函数直接复制内置类型成员的值,类类型成员使用该类的拷贝
构造函数进行复制,数组成员使用合成拷贝构造函数将复制数组的每一个元素

class A{
private:
 string s;
 int n;
 double b;
};

A::A(const A &a):s(a.s),n(a.n),b(a.b){}

定义自己的拷贝构造函数

class A{
public:
 A();
 A(const &A);
 //......
};

下面看一个类对象拷贝的简单例子:

#include<iostream>
using namespace std;

class A{
private:
 int n;
public:
 A(int m) { n=m;}
 void print()
 {
 cout<<n<<endl;
 }
};
int main()
{
 A a(100);
 A a1=a;
 a1.print();
 return 0;
}

运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象a1分配了内存并完成了与对象a的拷贝过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。下面举例说明拷贝函数的工作过程:

#include<iostream>
using namespace std;
class A{
private:
 int n;
public:
 A(int m)
 { n=m;}
 A(const A& c) {n=c.n;}
 void print()
 {
 cout<<n<<endl;
 }
};
int main()
{
 A a(100);
 A a1=a;
 a1.print();
 return 0;
}

A(const A& c)就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。例如:类X的拷贝构造函数的形式为X(X& x)。

当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化

如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝,后面将进行说明。自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。

浅拷贝和深拷贝

 在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员
变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行
错误。

 深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。下面举个深拷贝的例子:

#include<iostream>
#include<cstring>
#include<string>
using namespace std;
class A{
private:
 int n;
 char *s;
public:
 A(int m, char* str)
 {
 n=m;
 s=new char[m];
 strcpy(s,str);
 }
 A(const A& c)
 {
 n=c.n;
 s=new char[n]; //深拷贝 if(s!=NULL)
 strcpy(s,c.s);
 }
 void print()
 {
 cout<<s<<endl;
 }
 ~A()
 {
 delete s;
 }
};
int main()
{
 A a(100,"string");
 A a1=a;
 a1.print();
 return 0;
}

深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候,这个过程就可以叫做深拷贝,反之对象存在资源,但复制过程并未复制资源的情况视为浅拷贝。

浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错。

拷贝构造函数的名称必须与类名称一致,函数的形式参数是本类型的一个引用变量,且必须是引用。当用一个已经初始化过了的自定义类类型对象去初始化另一个新
构造的对象的时候,拷贝构造函数就会被自动调用,如果你没有自定义拷贝构造函数的时候,系统将会提供给一个默认的拷贝构造函数来完成这个过程

禁止复制

拷贝构造函数是私有的,将不允许用户代码复制该类类型的对象,但是类的友元和成员仍可以进行复制,如果连类的友元和成员也禁止,可以声明一个private构造函数但是不对其定义

时间: 2024-11-19 07:32:42

C++拷贝构造函数的相关文章

拷贝构造函数和运算符重载

拷贝构造函数应用的场合由以下几个方面: 1 函数的参数是一个对象,并且是值传递方式 2 函数的返回值是一个对象,并且是值传递方式 3 用一个对象初始化另外一个对象 由此,当函数的参数或者返回值为一个对象时,使用的时候要小心,因为值传递的时候执行的是位拷贝,并不会调用对象的构造函数,也就是说生成的临时对象可能不是正确初始化的,这样就可能会出现一些意向不到的问题.当返回值是个对象和用一个对象初始化另外一个对象时的情况是相同的. 比如如下代码: #include <iostream> using n

关于拷贝构造函数和赋值运算符

重点:包含动态分配成员的类 应提供拷贝构造函数,并重载"="赋值操作符. 以下讨论中将用到的例子: class CExample { public: CExample(){pBuffer=NULL; nSize=0;} ~CExample(){delete pBuffer;} void Init(int n){ pBuffer=new char[n]; nSize=n;} private: char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源 int nSize

C++类对象的拷贝构造函数分析

对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=100;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. #include <iostream>using namespace std;class CA{ public: CA(int b) { a=b; } void Show () { cout<<a<<endl; } private: int a;};int main(){

C++类对象的复制-拷贝构造函数

在学习这一章内容前我们已经学习过了类的构造函数和析构函数的相关知识,对于普通类型的对象来说,他们之间的复制是很简单的,例如: int a = 10; int b =a; 自己定义的类的对象同样是对象,谁也不能阻止我们用以下的方式进行复制,例如: #include <iostream>using namespace std;class Test{public: Test(int temp) { p1=temp; }protected: int p1;};void main(){ Test a(9

c++中拷贝构造函数的参数类型必须是引用

如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构 造函数.因此拷贝构造函数的参数必须是一个引用   在C++中, 构造函数,拷贝构造函数,析构函数和赋值函数(赋值运算符重载)是最基本不过的需要掌握的知识. 但是如果我问你"拷贝构造函数的参数为什么必须使用引用类型?"这个问题, 你会怎么回答? 或许你会回答为了

c++怎样让返回对象的函数不调用拷贝构造函数

  我们知道拷贝构造函数有两种"默默"的方式被调用 1. 想函数传入 值参数 2. 函数返回 值类型 今天我们讨论函数返回值类型的情况. 得到结论是 1. 当对象有拷贝构造函数(系统为我们生成.或者我们自己写拷贝构造函数)可以被隐式调用时,函数返回时会使用拷贝构造函数. 2. 当对象的拷贝构造函数声明成为explicit(不能被隐式调用时),函数返回时会使用move构造函数. 先看开始的代码. #include <iostream> #include <memory&

c++类默认拷贝构造函数---浅复制

类默认的拷贝构造函数是浅复制,比如如下程序,类中指向的地址是一样的 #include <iostream> using namespace std; class Test { public: int *a; }; int main() { Test *t = new Test(); int aa = 1314520; t->a = &aa; Test *t2 = new Test(*t); cout<<t->a<<endl; cout<<

c++函数调用时拷贝构造函数和析构函数

问题描述 c++函数调用时拷贝构造函数和析构函数 先上代码 class A{ public: A(){cout<<""构造函数""<<endl;} ~A(){cout<<""析构函数""<<endl;} A(const A &a){cout<<""拷贝构造函数""<<endl;}; A& operat

qstring-QString的拷贝构造函数问题

问题描述 QString的拷贝构造函数问题 今天看到qstring.h文件中的QString 的拷贝构造函数是这样写的 inline QString::QString(const QString &other) : d(other.d) { Q_ASSERT(&other != this); d->ref.ref(); } 不太明白 Q_ASSERT(&other != this) 这一句,othrer!=this????这个异常再怎样的情况下会触发?能否举个例子 解决方案

c++-如何自定义拷贝构造函数

问题描述 如何自定义拷贝构造函数 如何自定义C++中的拷贝构造函数,什么时候需要自定义拷贝构造函数,为什么? 解决方案 一般而言,当你需要自定义析构函数的时候,你就需要自定义拷贝构造函数以及赋值运算符重载.析构函数一般在涉及到需要手动释放系统资源的时候就需要自定义,典型的例子就是指针. 解决方案二: 当你需要进行对象赋值初始化的时候,就需要定义拷贝构造,对于C++来说,这是经常发生的,所以自定义类一般都需要定义拷贝构造函数 解决方案三: //自定义拷贝构造函数classname(const cl