- 浏览: 429909 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
su6838354:
我有点疑问啊,thread1中的i自增的慢的话,thread2 ...
浅析pthread_cond_wait -
zeronever:
请问pthread_cond_signal有解锁操纵吗?我在p ...
浅析pthread_cond_wait -
paladin1988:
你这帖子真心不错。。
浅谈bitmap算法 -
parabellum_sky:
昨天还有个姑娘让我去考我说会考虑
个人日志
构造函数,析构函数和赋值操作符
几乎所有的对象都需要构造函数、拷贝构造函数、析构函数和赋值构造函数,但问题是何时需要自己定义这些函数,何时调用默认的这些函数?这也是所谓的“浅拷贝”和“深拷贝”的问题。
当类的数据成员有指针,需要动态的为指针申请内存时,这时就需要自己定义拷贝构造函数和赋值构造函数,这就是所谓的“深拷贝”。
当类的数据成员没有指针,不需要动态的为指针申请内存时,不需要定义自己版本的拷贝和赋值构造函数,直接调用默认的即可,这就是所谓的“浅拷贝”。
为何这种情况需要定义自己的拷贝构造函数和赋值构造函数?看下面的例子:
#include <iostream> using namespace std; // 一个很简单的TString类 class TString { public: TString(const char *value); ~TString(); void Print(); private: char *data; }; TString::TString(const char *value) { if (value) { data = new char[strlen(value) + 1]; strcpy(data, value); } else { data = new char[1]; *data = '\0'; } cout << "Call Constructor !\n"; } inline TString::~TString() { delete [] data; cout << "Call Destructor !\n";} void TString::Print() { cout << "The content is :" << data << endl; } void donothing(const TString &localstring) { cout << "Call donothing function!\n";} int main() { TString a("hello"); TString b("world"); //b = a; b.Print(); a.Print(); }
输出为:
Call Constructor !
Call Constructor !
The content is :world
The content is :hello
Call Destructor !
Call Destructor !
但是如果定义:b = a; 就会提示错误,为何呢?类本身不是提供默认的赋值构造函数吗,为何还会出错?
如果b = a;而没有定义自己的赋值构造函数,TString b("world");为b对象申请的内存段将丢失,也即没有任何指针指向这个内存段,这就是所谓的内存泄露。而b = a;会通过默认的赋值构造函数直接把指针a所指向的地址赋值给b,那么b和a指向同一段地址,当a和b离开生存空间时会调用类的析构函数来删除申请的地址,比如当b删除了b.data所指向的地址后,a对象也会调用析构函数删除a.data指向的地址,但此地址已经被b对象删除,用delete删除一个已经被删除的指针,其结果是不可预料的。
这就是为什么当类的数据成员是指针时,需要字定义拷贝构造函数和赋值构造函数。
又如:
TString s = "the truth is out there";
donothing(s);
如果没有定义自己的拷贝构造函数,会出现同样的问题,说明如下:
一切好象都很正常。但因为被传递的localstring是一个值,它必须从s通过(缺省)拷贝构造函数进行初始化。于是localstring拥有了一个s内的指针的拷贝。当donothing结束运行时,localstring离开了其生存空间,调用析构函数。其结果也将是:s包含一个指向localstring早已删除的内存的指针。
解决这类指针混乱问题的方案在于,只要类里有指针时,就要写自己版本的拷贝构造函数和赋值操作符函数。在这些函数里,你可以拷贝那些被指向的数据结构,从而使每个对象都有自己的拷贝;或者你可以采用某种引用计数机制(见条款 m29)去跟踪当前有多少个对象指向某个数据结构。引用计数的方法更复杂,而且它要求构造函数和析构函数内部做更多的工作,但在某些(虽然不是所有)程序里,它会大量节省内存并切实提高速度。
在看下面的例子:
#include <iostream> using namespace std; class CSomething { public: int a; int b; public: CSomething(int a, int b) { cout << "Call CSomething Constructor !" << endl; this->a = a; this->b = b; } ~CSomething() { cout << "Call the CSomething Destructor!" << endl; } }; class CA { private: CSomething* sth; // 以指针形式存在的成员变量 public: CA(CSomething* sth) { cout << "Call CA Constructor!" << endl; this->sth = new CSomething(sth->a, sth->b); } ~CA() { cout << "In the destructor of class CA..." << endl; if (NULL != sth) delete sth; } void Show(){cout << "(" << sth->a << ", " << sth->b << ")" << endl;} void setValue(int a, int b){sth->a = a; sth->b = b;} void getSthAddress() { cout << sth << endl; } }; int main(void) { CSomething sth(1, 2); CA ca(&sth); ca.Show(); CA cb(ca); // 调用缺省的隐式拷贝构造函数 cb.Show(); cb.setValue(2, 3); ca.Show(); cb.Show(); ca.getSthAddress(); cb.getSthAddress(); return 0; }
没有显示定义拷贝构造函数,其结果输出如下:
Call CSomething Constructor !
Call CA Constructor!
Call CSomething Constructor !
(1, 2)
(1, 2)
(2, 3)
(2, 3)
00396560
00396560
In the destructor of class CA...
Call the CSomething Destructor!
In the destructor of class CA...
Call the CSomething Destructor!
由输出结果可以看出:ca和cb的sth是同一个。
运行时会出错,其出错原因时程序结束时调用析构函数delete sth,由于ca.sth地址:00396560 和cb.sth地址:00396560相同,当ca.sth删除掉后,cb.sth再删除时就会出现问题。
下面是加上CA的拷贝构造函数:
CA( const CA & ob) { sth = new CSomething((ob.sth)->a,(ob.sth)->b); cout << "Call the Copy Constructor!" << endl; }
其输出为:
Call CSomething Constructor !
Call CA Constructor!
Call CSomething Constructor !
(1, 2)
Call CSomething Constructor !
Call the Copy Constructor!
(1, 2)
(1, 2)
(2, 3)
00396560
00396610
In the destructor of class CA...
Call the CSomething Destructor!
In the destructor of class CA...
Call the CSomething Destructor!
Call the CSomething Destructor!
由输出结果:
修改值后的ca.sth->a ca.sht->b
(1, 2)
修改值后的cb.sth->a cb.sht->b
(2, 3)
ca.sth:00396560
cb.sth00396610
可以看出达到了”深拷贝“的效果。
发表评论
-
Google编程风格
2012-04-01 17:05 1073Google编程风格(自己整 ... -
VS2008快捷键的设置
2012-02-02 13:43 2611VS2008快捷键的设置 VS2008默认的快捷键和VC++ ... -
LINK : fatal error LNK1000: Internal error during IncrBuildImage
2011-12-16 17:07 1187Win7安vc2008编译报LINK : fatal erro ... -
volatile关键字(摘自:百度百科)
2011-12-16 14:59 700volatile关键字[align=center][/alig ... -
InterlockedIncrement
2011-12-16 14:44 2147InterlockedIncrement[align=cent ... -
关键词explicit
2011-12-10 20:32 823关键词explicit[size=large][/size][ ... -
Souce Insight 设置
2011-12-09 17:16 1954Souce Insight 设置 【问题】 Source I ... -
VS2008下Boost库的安装编译下载boost库
2011-11-09 19:19 2011下载boost库 (最好去官网下,一般有SGI(GCC+用的较 ... -
C++多态技术的实现和反思(转)
2011-10-17 17:05 774面向对象技术最早出现于1960年代的Simula 67系统,并 ... -
memmove and memcpy
2011-07-31 13:11 1150memmove and memcpy 字符串的拷贝函数mem ... -
Polymorphism & Virtual Function
2011-07-25 21:38 826Polymorphism & Virtual Func ... -
C++ 不要重新定义继承的非虚函数
2011-07-18 14:15 1268不要重新定义继承的非虚函数 如果基类和派生类有相同的非虚函数 ... -
C++ 虚函数表解析
2011-07-14 21:02 947C++ 虚函数表解析 为什么在C++机制里要有虚函数表?虚函 ... -
C++ 默认构造函数
2011-07-14 11:39 3692C++ 默认构造函数 一直 ... -
尽量使用const
2011-07-12 10:51 1079尽可能的使用const const: ... -
Operator=
2011-07-11 21:54 912Operator= 赋值构造函数 ... -
虚析构函数
2011-07-11 10:43 1134虚析构函数 一 、为何要单独讨论虚析构函数?虚函数在类中到底 ... -
初始化列表和声明顺序之间的关系
2011-07-11 09:18 1441初始化列表和声明的顺序之间的关系 类中数据成员的声明顺序和初 ... -
初始化函数列表和构造函数内赋值之区别
2011-07-10 17:05 4470初始化函数列表和构造 ... -
传值和传引用的区别
2011-07-10 15:30 2348传值和传引用的区别 在C语言中,大都是通过值传递,C++也是 ...
相关推荐
C++构造函数_析构函数和赋值操作符学习小结
详解C++ 编写String 的构造函数、拷贝构造函数、析构函数和赋值函数 编写类String 的构造函数、析构函数和赋值函数,已知类String 的原型为: class String { public: String(const char *str = NULL); // 普通...
而当调用函数中有一个接受对象时,就将返回对象赋值给接收对象,这个返回对象在调用函数结束时调用析构函数。3. 当类有一个带有一个参数的构造函数时,可以用这个参数同类型的数据初始化这个对象,默认会调用这个...
我们都知道,在C++中建立一个类,这个类中肯定会包括构造函数、析构函数、复制构造函数和重载赋值操作;即使在你没有明确定义的情况下,编译器也会给你生成这样的四个函数。例如以下类: 代码如下: class CTest { ...
初始化和赋值问题详解C++ 拷贝构造函数和赋值运算符详解C++中对构造函数和赋值运算符的复制和移动操作C++中复制构造函数和重载赋值操作符总结深入C++中构造函数、拷贝构造函数、赋值操作符、析构函数的调用过程总结...
在C++中复制控制是一个比较重要的话题,主要包括复制构造函数、重载赋值操作符、析构函数这三部分,这三个函数是一致的,如果需要手动定义了其中了一个,那么另外的两个也需要定义,通常在存在指针或者前期相关操作...
一个典型的String类实现,C++描述,里面包括String的构造函数,赋值构造函数,析构函数,赋值操作符的实现等
10.13章 复制构造函数和赋值操作符 11.13章 析构函数 12.13章 深复制、浅复制 13.13章 管理指针成员 14.14章 重载操作符的定义 15.14章 重载输入输出操作符 16.14章 重载算术操作符 17.14章 重载关系...
《C++面向对象高级编程》测试题为下面的 Rectangle 类实现构造函数,拷贝构造函数,赋值操作符,析构函数。注:希望大家在一周内上传答案到极客班 gith
格式 .txt,方便阅读,编辑,打印 01.第一章 从C转向C++.txt 02.第二章 内存管理.txt 03.第三章 构造函数,析构函数和赋值操作符.txt ... 有空多去去 http://meooo.download.csdn.net/ 也许有你喜欢东西和书籍......
这将不需要在派生类中显式的定义析构函数,复制构造函数,赋值操作符。如果在派生类中没有定义析构函数,编译器将定义一个不执行任何操作的默认构造函数,实际上,派生类的默认构造函数总要进行一些操作:执行...
第三章 构造函数,析构函数和赋值操作符 条款11: 为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符 条款12: 尽量使用初始化而不要在构造函数里赋值 条款13: 初始化列表中成员列出的顺序和它们在类中声明...
派生类是否需要为显示定义析构函数,复制构造函数和赋值操作符呢? 不需要! 首先,来看是否需要析构函数,如果没有定义析构函数,编译器将定义一个不执行任何操作的默认构造函数。实际上,派生类的默认构造函数...
●赋值操作符,如果没有定义 ●地址操作符 class Stack { private: char *str; public: Stack(); //默认构造函数 ~Stack();//析构函数 Stack (const Stack &); //复制构造函数 Stack (char *str ) /...
std::array的构造函数、析构函数和赋值操作符都是编译器隐式声明的……这让很多用惯了std::vector这类容器的程序员不习惯,觉得std::array不好用。 但实际上,std::array的威力很可能被低估了。在
对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数减至0,则删除对象),并增加右操作数所指对象的引用计数; 调用析构函数时,减少引用计数(如果引用计数减至0,则删除基础对象); ...
5.2 关系操作符和逻辑操作符 131 5.3 位操作符 134 5.3.1 bitset对象或整型值的使用 135 5.3.2 将移位操作符用于IO 137 5.4 赋值操作符 137 5.4.1 赋值操作的右结合性 138 5.4.2 赋值操作具有低优先级 138 5.4.3 ...
智能指针是存储指向动态分配(堆)对象指针的类, 用于生存期控制, 能够确保自动正确的销毁动态分配的对象,防止内存泄露。...调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。
2.虽然结构的初始化也使用了New 操作符可是结构对象依然分配在堆栈上而不是堆上,如果不使用“新建”(new),那么在初始化所有字段之前,字段将保持未赋值状态,且对象不可用 2.继承性 结构:不能从另外一个结构...