C++构造函数和拷贝构造函数详解
构造函数、析构函数与赋值函数是每个类最基本的函数。它们太普通以致让人容易麻痹大意,其实这些貌似简单的函数就象没有顶盖的下水道那样危险。
每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。
对于任意一个类A,如果不想编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如:
A(void); // 缺省的无参数构造函数
A(const A &a); // 缺省的拷贝构造函数
~A(void); // 缺省的析构函数
A & operate =(const A &a); // 缺省的赋值函数
这不禁让人疑惑,既然能自动生成函数,为什么还要程序员编写?原因如下:
<1>如果使用“缺省的无参数构造函数”和“缺省的析构函数”,等于放弃了自主“初始化”和“清除”的机会,C++发明人Stroustrup 的好心好意白费了。
<2>“缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量,这两个函数注定将出错。
C++ 默认构造函数 :
1、每个类必须有一个构造函数,否则没法创建对象;
2、若 程序员没有提供任何构造函数,则
C++提供一个默认的构造函数,该默认构造函数是无参构造函数,它仅负责创建对象,不做任何初始化的工作;
3、只要 programer
定义了一个构造函数(不管是无参还是有参构造),C++就不再提供默认的默认构造函数。即如果为类定义了一个带参的构造函数,还想要无参构造函数,就必须自己定义;
4、与变量定义类似,在用默认构造函数创建对象时,如果创建的是全局对象或静态对象,则对象的位模式全为
0,否则,对象值是随机的。
C++默认拷贝构造函数:
1、默认的拷贝构造函数执行的顺序与其他用户定义的构造函数相同,执行先父类后子类的构造.
2、拷贝构造函数对类中每一个数据成员执行成员拷贝(memberwise
Copy)的动作.
3、如果数据成员为某一个类的实例,那么调用此类的拷贝构造函数.
4、如果数据成员是一个数组,对数组的每一个执行按位拷贝.
5、如果数据成员是一个数量,如int,double,那么调用系统内建的赋值运算符对其进行赋值.
请看下面代码:
#include <iostream>
#include <string>
using namespace std; class Student
{
public:
Student()
{
cout << "构造函数1" << endl;
}
Student(int k)
{
cout << "构造函数2" << endl;
i = k;
}
Student(Student const &m)
{
cout << "拷贝构造函数" << endl;
i = m.i * (-1);
} void p()
{
cout << i << endl;
}
~Student()
{
cout << "析构函数" << endl;
}
protected:
int i;
}; int main(int argc, char **argv)
{
Student s(9818);
// 调用构造函数2
s.p(); Student t(s);
// 调用拷贝构造函数
t.p(); Student k = s;
// 调用拷贝构造函数
k.p(); Student *p = new Student(s);
// 调用拷贝构造函数
p->p(); Student m;
// 调用构造函数1
m = s;// 赋值运算
m.p(); return 0;
}
运行结果:
构造函数2 拷贝构造函数
-
拷贝构造函数
-
拷贝构造函数
-
构造函数1 析构函数
析构函数
析构函数
析构函数
下面我们来讨论一下关于浅拷贝和深拷贝的问题。
深拷贝和浅拷贝的定义可以简单理解成:如果一个类拥有资源(堆,或者是其它系统资源),当这个类的对象发生复制过程的时候(复制指针所指向的值),这个过程就可以叫做深拷贝,反之对象存在资源但复制过程并未复制资源(只复制了指针所指的地址)的情况视为浅拷贝。
很多人会问到,既然系统会自动提供一个默认的拷贝构造函数来处理复制,那么我们没有必要去自定义拷贝构造函数呀,对,就普通情况而言这的确是没有必要的,但在某些状况下,类体内的成员是需要开辟动态堆内存的,如果我们不自定义拷贝构造函数而让系统自己处理,那么就会导致堆内存的所属权产生混乱。试想一下,已经开辟的一端堆地址原来是属于对象a的,由于复制过程发生,b对象取得是a已经开辟的堆地址,一旦程序产生析构,释放堆的时候,计算机不清楚这段地址是真正属于谁的,当连续发生两次析构的时候就出现了运行错误。
为了更详细的说明问题,请看如下的代码。
#include <iostream>
#include <string>
using namespace std; class aa
{
public:
aa()
{
cout << "调用构造函数" << endl;
f = new char[];
}
~aa()
{
cout << "调用析构函数" << endl;
delete[] f;
}
char * f;
}; int main(int argc, char **argv)
{
aa p;
printf("p.f=%p\n",p.f);
strcpy(p.f, "Computer");
cout << p.f << endl;
aa q(p);// 调用默认的拷贝构造函数
printf("q.f=%p\n",q.f);
cout << q.f << endl; strcpy(p.f, "Software");
cout << p.f << endl;
cout << q.f << endl; strcpy(q.f, "Software");
cout << p.f << endl;
cout << q.f << endl; return ;
}
运行结果:
调用构造函数
p.f=003F1048
Computer
q.f=003F1048
Computer
Software
Software
Software
Software
调用析构函数
调用析构函数
通过上面的例子,我们能清楚的看到,第二个对象调用拷贝构造函数,q.f获得的地址值与p.f相同,即指向了同一片内存区域。程序结束时,会两次调用析构函数,即同一个指针执行两遍delete操作,会发生什么呢?这可能是一场灾难,可能会破坏该堆及自由内存表。
那我们该如何避免呢?这里我们就需要使用深拷贝。
#include <iostream>
#include <string>
using namespace std; class aa
{
public:
aa()
{
cout << "调用构造函数" << endl;
f = new char[];
}
aa(aa const & s)
{
cout << "调用拷贝构造函数" << endl;
f = new char[];
strcpy(f, s.f);
}
~aa()
{
cout << "调用析构函数" << endl;
delete[] f;
}
char * f;
}; int main(int argc, char **argv)
{
aa p;
printf("p.f=%p\n", p.f);
strcpy(p.f, "Computer");
cout << p.f << endl;
aa q(p);// 调用用戶的拷贝构造函数
printf("q.f=%p\n", q.f);
cout << q.f << endl; strcpy(p.f, "Software");
cout << p.f << endl;
cout << q.f << endl; strcpy(q.f, "Software");
cout << p.f << endl;
cout << q.f << endl; return ;
}
运行结果:
调用构造函数
p.f=
Computer
调用拷贝构造函数
q.f=
Computer
Software
Computer
Software
Software
调用析构函数
调用析构函数
现在我们可以看到,p.f和q.f分别指向不同的内存区域。
来自:http://blog.csdn.net/sg131971/article/details/7045278
C++构造函数和拷贝构造函数详解的更多相关文章
- 【转】 c++拷贝构造函数(深拷贝,浅拷贝)详解
c++拷贝构造函数(深拷贝,浅拷贝)详解 2013-11-05 20:30:29 分类: C/C++ 原文地址:http://blog.chinaunix.net/uid-28977986-id-3 ...
- 【C++】拷贝构造函数(深拷贝,浅拷贝)详解
一.什么是拷贝构造函数 首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: ; int b = a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量. 下面看一个类对 ...
- C++ 构造函数、拷贝构造函数、赋值运算符
<C++ Primer Plus> 12.1 动态内存和类 12.1.1 复习示例和静态类成员 不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但并不分配内存 如果在头文 ...
- c++ 构造函数,拷贝构造函数,析构函数与赋值操作符
题目: 为下面的Rectangle类实现构造函数,拷贝构造函数,赋值操作符,析构函数. class Shape { int no; }; class Point { int x; int y; }; ...
- 类string的构造函数、拷贝构造函数和析构函数
原文:http://www.cnblogs.com/Laokong-ServiceStation/archive/2011/04/19/2020402.html 类string的构造函数.拷贝构造 ...
- C++中的构造函数,拷贝构造函数和赋值运算
关于C++中的构造函数,拷贝构造函数和赋值运算,以前看过一篇<高质量C++/C编程指南>的文章中介绍的很清楚,网上能搜索到,如果想详细了解这方面的知识可以参看一下这篇文章. 常见的给对象赋 ...
- 编写类String的构造函数、拷贝构造函数、析构函数和赋值函数
一.题目: class String { public: String(const char *str = NULL); // 普通构造函数 String(const String &othe ...
- CPP_类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数
类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数 // person.h #ifndef _PERSON_H_ #define _PERSON_H_ class Person{ public : ...
- C++中构造函数,拷贝构造函数和赋值函数的区别和实现
C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象 ...
随机推荐
- Server.xml配置详解
<Server port="8005" shutdown="SHUTDOWN"> <!-- 属性说明 port:指定一个端口,这个端口负责监听 ...
- iOS 后台返回json解析出现的null的解决办法
在后台返回值为Null为空时,我们代码没有判断时,程序就会崩溃.当时一直很疑惑是为啥,后来发现是数据问题,由于服务器的数据库中有些字段为空,然后以Json形式返回给客户端时就会出现这样的数据.当我们通 ...
- 目标检测之积分图---integral image 积分图2
前面在图像处理一栏中涉及到boxfilter 的时候,简单介绍过积分图,就是每个像素点是左边和上边的累加和,这样的话可以方便均值和方差,以及直方图统计的相关运算,这里再次结合网络资源重新单独对积分图做 ...
- 谈谈加载(Loading)的那点事
谈谈加载(Loading)的那点事 2013/10/12 | 分类: 设计 | 0 条评论 | 标签: 交互设计, 加载 分享到:33 原文出处: 搜狐焦点 对于加载(loading),想必大家都不陌 ...
- BestCoder #47 1001&&1002
[比赛链接]cid=608">clikc here~~ ps:真是wuyu~~做了两小时.A出两道题,最后由于没加longlong所有被别人hack掉!,最后竟然不知道hack别人不成 ...
- iOS非常全的第三方库
iOS ● 非常全的三方库.插件.大牛博客等等 github排名:https://github.com/trending, github搜索:https://github.com/search. ...
- RS485总线典型电路介绍
一.RS485总线介绍: RS485总线是一种常见的串行总线标准,采用平衡发送与差分接收的方式,因此具有抑制共模干扰的能力.在一些要求通信距离为几十米到上千米的时候,RS485总线是一种应用最为广泛的 ...
- 【BZOJ3193】[JLOI2013]地形生成 DP
[BZOJ3193][JLOI2013]地形生成 Description 最近IK正在做关于地形建模的工作.其中一个工作阶段就是把一些山排列成一行.每座山都有各不相同的标号和高度.为了遵从一些设计上的 ...
- appium()-java-client-api
//appium java-client-api 介绍 原文地址:http://appium.github.io/java-client/index-all.html#_S_ A B C D E F ...
- GCD 开启一个定时器实现倒计时功能
UIAlertView * alt = [[UIAlertView alloc] initWithTitle:@"提示" message:@"操作成功,马上返回继续体验吧 ...