C++中的构造函数,拷贝构造函数,赋值函数
C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法。下面就详细比较下三者之间的区别以及它们的具体实现
1.构造函数
构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存。(构造函数的命名必须和类名完全相同)
首先说一下一个C++的空类,编译器会加入哪些默认的成员函数
·默认构造函数和拷贝构造函数
·析构函数
·赋值函数(赋值运算符)
·取值函数
**即使程序没定义任何成员,编译器也会插入以上的函数!
构造函数可以被重载,可以多个,可以带参数;
析构函数只有一个,不能被重载,不带参数
而默认构造函数没有参数,它什么也不做。当没有重载无参构造函数时,
A a就是通过默认构造函数来创建一个对象
2.拷贝构造函数
拷贝构造函数是C++独有的,它是一种特殊的构造函数,用基于同一类的一个对象构造和初始化另一个对象。
当没有重载拷贝构造函数时,通过默认拷贝构造函数来创建一个对象
A a;
A b(a);
A b=a; 都是拷贝构造函数来创建对象b
强调:这里b对象是不存在的,是用a 对象来构造和初始化b的!!
先说下什么时候拷贝构造函数会被调用:
在C++中,3种对象需要复制,此时拷贝构造函数会被调用
1)一个对象以值传递的方式传入函数体
2)一个对象以值传递的方式从函数返回
3)一个对象需要通过另一个对象进行初始化
系统提供的默认拷贝构造函数工作方式是内存拷贝,也就是浅拷贝。这种方法如果对象中用到了需要手动释放的对象(如指针),则会出现问题。
下面说说深拷贝与浅拷贝:
浅拷贝:如果复制的对象中引用了一个外部内容(例如分配在堆上的数据),那么在复制这个对象的时候,让新旧两个对象指向同一个外部内容,就是浅拷贝。(指针虽然复制了,但所指向的空间内容并没有复制,而是由两个对象共用,两个对象不独立,删除空间存在)浅拷贝复制了一份对象,只复制了对象的本身
深拷贝:如果在复制这个对象的时候为新对象制作了外部对象的独立复制,就是深拷贝。深拷贝,把空间也拷贝了一份
默认的浅拷贝的情况:
class cperson
{
public :
int *a;//当a是整型变量时就不会发生浅拷贝问题
public :
cperson(const cperson &aa)
{
this->a=aa.a;
}
~cperson()
{
delete a;
a=NULL;
} };
cperson ps(ps1);//会出现错误,程序会崩溃
原因就是浅拷贝,只复制了对象本身的内容,指针成员所指向的空间是没有拷贝的,会出现两个对象使用一个空间从而导致在释放空间的时候一个空间被释放两次
解决的方法:
1.使用深拷贝,如下例子:
class cperson
{
public :
int *a;
public :
cperson(const cperson &aa)
{
this->a=new int (*(aa.a));//深拷贝
}
~cperson()
{
delete a;
a=NULL;
} };
2.不让它执行拷贝构造,即对象的函数传参时不要用值传递,要用引用如下面的例子
void qq(cperson ps1)
{ }
cperson ps;
qq(ps);//发生了浅拷贝,程序错误
用引用代替后
void qq(cperson &ps1)
{ }
cperson ps;
qq(ps);//程序正确
特别要注意的是下面这种情况:
cperson qq(cperson &ps1)
{
cperson ps2;
return ps2;
}
原因是为了保证能把ps2的值拿出去,ps2是临时对象,要想把它传出去就把它的值复制出去,此时又发生浅拷贝了。可以用返回一个引用解决。
cperson &qq(cperson &ps1)
{
cperson ps2;
return ps2;
}
但实际上尽量不要返回局部对象。
3.赋值函数
当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值函数。
当没有重载赋值函数(赋值运算符)时,通过默认赋值函数来进行赋值操作
A a;
A b;
b=a;
强调:这里a,b对象是已经存在的,是用a 对象来赋值给b的!!
赋值运算的重载声明如下:
A& operator = (const A& other),我们先来看一下下面的例子
class cperson
{
public :int *a;
public :
cperson()
{
a=new int(100);
}
cperson & operator=(const cperson&ps)
{
return *this;
}
~cperson()
{
delete a;
a=NULL;
};
cperson ps;
cperson ps1;
ps1=ps;
该程序会崩溃,原因是对象中含有指针,当用一个对象给另一个对象赋值的时候,又出现了两个指针使用同一块地址空间的问题,那么如何解决呢?
我们可以通过先删除原来指针的空间,再为被赋值的对象中的指针重新分配空间,如下述代码:
class cperson
{
public :int *a;
public :
cperson()
{
a=new int(100);
}
cperson & operator=(const cperson&ps)
{
delete this->a;
this->a=new int (*(ps.a));
return *this; }
~cperson()
{
delete a;
a=NULL;
};
cperson ps;
cperson ps1;
ps1=ps;
通常大家会对拷贝构造函数和赋值函数混淆,这儿仔细比较两者的区别:
1)拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。
2)一般来说在数据成员包含指针对象的时候,需要考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象
3)实现不一样。拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。
赋值函数则是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检察一下两个对象是不是同一个对象,如果是,不做任何操作,直接返回。
!!!如果不想写拷贝构造函数和赋值函数,又不允许别人使用编译器生成的缺省函数,最简单的办法是将拷贝构造函数和赋值函数声明为私有函数,不用编写代码。:
所以如果类定义中有指针或引用变量或对象,为了避免潜在错误,最好重载拷贝构造函数和赋值函数。
一句话记住三者:对象不存在,且没用别的对象来初始化,就是调用了构造函数;
对象不存在,且用别的对象来初始化,就是拷贝构造函数(上面说了三种用它的情况!)
对象存在,用别的对象来给它赋值,就是赋值函数。
本文转载于:http://blog.csdn.net/zcyzsy/article/details/52132936
C++中的构造函数,拷贝构造函数,赋值函数的更多相关文章
- 【c++ primer, 5e】构造函数 & 拷贝、赋值和析构
[构造函数] 1.构造器就是创建对象时被调用的代码. 2.如果没有自定义构造器,那么编译器将自动合成一个默认的无参构造器. 3.自定义的构造器不允许加const,所创建const的对象只有在构造器代码 ...
- 关于C++ 中 thread 的拷贝构造函数
起因来自于<C++并发编程实战>的这样一个例子 #include <thread> #include <iostream> #include <stdexce ...
- C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数
构造函数 ,是一种特殊的方法 .主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 .特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数 ...
- Python中的深浅拷贝,赋值及引用
简单来说,若对象a中存的是列表或字典等可变对象,b对a的浅拷贝只是对对象第一层的复制,修改b第二层的元素仍然会影响两个对象. 深拷贝则是不会影响原来的对象. import copy.copy() 浅拷 ...
- C++中构造函数,拷贝构造函数和赋值函数的区别和实现
C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象 ...
- 【C++】拷贝构造函数和赋值符函数
在C++中,调用拷贝构造函数有三种情况: 1.一个对象作为函数参数,以值传递的方式传入函数体. 2.一个对象作为函数返回值,以值传递的方式从函数返回. 3.一个对象用于给另外一个对象进行初始化(复制初 ...
- C++中复制构造函数与重载赋值操作符总结
前言 这篇文章将对C++中复制构造函数和重载赋值操作符进行总结,包括以下内容: 1.复制构造函数和重载赋值操作符的定义: 2.复制构造函数和重载赋值操作符的调用时机: 3.复制构造函数和重载赋值操作符 ...
- C++中复制构造函数与重载赋值操作符
我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数.析构函数.复制构造函数和重载赋值操作:即使在你没有明确定义的情况下,编译器也会给你生成这样的四个函数.例如以下类: class CTe ...
- C++ 类 & 对象-类成员函数-类访问修饰符-C++ 友元函数-构造函数 & 析构函数-C++ 拷贝构造函数
C++ 类成员函数 成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义. 需要强调一点,在 :: 运算符之前必须使用类名.调用成员函数是在对象上使用点运算符(.),这样它就能操作与 ...
随机推荐
- DNS服务器原理介绍(一)
DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串.通过主机名,最终 ...
- phpstorm常用快捷键(自备不全)
CTRL+N 查找类 CTRL+SHIFT+N 全局搜索文件 ,优先文件名匹配的文件 CTRL+SHIFT+ALT+N 查找php类名/变量名 ,js方法名/变量名, css 选择器 CTRL+G 定 ...
- c++中对应java ShutdownHook的退出处理器
最近学习cpp(至于为什么,可参考http://www.cnblogs.com/zhjh256/p/6321972.html),c++标准库第二版5.8.2节的时候,发现c++有一个对应java Sh ...
- python程序转为exe文件
python开发者向普通windows用户分享程序,要给程序加图形化的界面(传送门:这可能是最好玩的python GUI入门实例! http://www.jianshu.com/p/8abcf73ad ...
- Hadoop错误日志
1.错误日志:Directory /tmp/hadoop-root/dfs/name is in an inconsistent state: storage directory does not e ...
- Python3基础 print \n换行
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- Python3基础 pickle.dump和load 对一个对象进行序列化存储及读取
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- 快速排序|2018年蓝桥杯B组题解析第五题-fishers
标题:快速排序 以下代码可以从数组a[]中找出第k小的元素. 它使用了类似快速排序中的分治算法,期望时间复杂度是O(N)的. 请仔细阅读分析源码,填写划线部分缺失的内容. #include <s ...
- ActiveMQ、RabbitMQ、RocketMQ、Kafka 对比(图示)
RabbitMQ 和 Kafka 对比,一篇好的介绍文章:https://my.oschina.net/u/236698/blog/501834 ActiveMQ.RabbitMQ.RocketMQ. ...
- 卸载vs2017
卸载enterprise版本 Microsoft.FSharp.SDK.Core卸载失败Package 'Microsoft.FSharp.SDK.Core,version=15.7.20180605 ...