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.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象 ...
随机推荐
- Möbius strip
en.wikipedia.org/wiki/Möbius_strip http://mechproto.olin.edu/final_projects/average_jo.html Fabricat ...
- python网络爬虫之使用scrapy爬取图片
在前面的章节中都介绍了scrapy如何爬取网页数据,今天介绍下如何爬取图片. 下载图片需要用到ImagesPipeline这个类,首先介绍下工作流程: 1 首先需要在一个爬虫中,获取到图片的url并存 ...
- Nodejs通过Thrift操作hbase卡住原因分析及与javascript的垃圾回收机制的关系
在最近使用Nodejs通过Thrift操作hbase的时候写了个脚本,不断发送http请求,从而取得hbase下所需的数据,但是在run的过程中for循环并没有执行完全,在执行一部分后会卡住,就再也进 ...
- include vector 编译出错VC++
error C2665: “operator new” : 5个重载中没有一个可以转换参数1(从“const char [71]”类型)这个错误是怎么回事啊,搜索了整个项目好像没有可疑的new操作阿. ...
- DHCP request error:Timed out waiting for dhcpcd to start【转】
本文转载自:http://blog.csdn.net/zvivi521/article/details/9166899 [init.svc.dhcpcd_eth0]: [stopped] I/Serv ...
- 步入C编程的第一天
我想学ruby以后开发网站,但ruby是高级语言,隐藏了许多底层的东西,因此先熟悉c语言 首先c程序的文件名是以.c结尾的 c程序的格式: 第一行#include<stdio.h> #是一 ...
- hdu-5761 Rower Bo(数学)
题目链接: Rower Bo Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others ...
- [Codeforces 715C] Digit Tree
[题目链接] https://codeforces.com/contest/715/problem/C [算法] 考虑点分治 一条路径(x , y)合法当且仅当 : d(x) * 10 ^ dep(x ...
- [ZJU 2112] Dynamic Rankings
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1901 [算法] 首先 , 考虑没有修改操作 不妨建立可持久化线段树 , 第i棵树维护 ...
- AtCoder Grand Contest 007 E:Shik and Travel
题目传送门:https://agc007.contest.atcoder.jp/tasks/agc007_e 题目翻译 现在有一个二叉树,除了叶子每个结点都有两个儿子.这个二叉树一共有\(m\)个叶子 ...