C++:类的设计————构造与析构函数及其动态内存管理
来源:互联网 发布:天池大数据竞赛官网 编辑:程序博客网 时间:2024/04/30 02:11
构造与析构函数
1.构造与析构函数的意义
创建对象是往往需要初始化,但是对象不可以直接访问私有成员,因此C语言中直接赋值的初始化方法行不通,于是C++使用构造函数进行对象的初始化操作。
构造函数的原型声明在public中,名称与类名一致,即:
class class_name{ private: .. public: class_name();};
C++规定,如果程序员不自定义构造函数,会给出一个默认的构造函数(需要注明的是构造函数没有声明类型),默认的构造函数没有任何功能,还需要程序员添加相应的操作。
例如下面这个类:
class Bank{ private: int client; double money; public: Bank();//default Bank(int c,double m); ~Bank();};Bank::Bank(){ client = 0; money = 0.0;}
注意到上述例子中,有Bank()的一个重载版本Bank(int c,double m),这也是一个构造函数,是程序员自定义的,带有参数列表,这样,我们在创建新的对象的时候,就可以这样写:
Bank bank1;//不初始化调用默认构造函数Bank bank2(500,1000.0);//隐式的使用Bank(int c,double m)Bank bank3 = Bank(200,50.0);//显式的使用Bank(int c,double m)
有意思的是,如果类的声明仅仅给出了Bank(int c,double m)而没有Bank()时,编译器会对bank1报错。原因在于,只有当程序员不定义任何构造函数时C++才会给出默认的构造函数,当自定义了构造函数时必须定义默认的构造函数,这样意在禁止创建未初始化的变量。
复制构造函数
还有一种特殊的构造函数,被称为复制构造函数,一般来说,这也是C++自己默认给出的。
所谓的复制构造函数 实际上是对赋值号(=)的一种规则约定,它仅用于初始化过程中,它的原型如下:
class_name (const class_name &)
例如属于Time类的复制构造函数
Time (const Time & time)
不同于普通的自定义构造函数,参数只有一个且类型和类的名称统一。
实际上,复制构造函数仅用于初始化过程中包含两层含义。
一个是常规的初始化对象,还有一个是指函数按值传递对象。
前一个很好理解,那么后一个是怎么回事?
实际上,同前一个一样,函数按值传递对象是,会先生成一个临时变量,将原始对象的值赋值给临时变量,此时会调用复制构造函数。
有时候我们回想更改赋值规则,那么可以自己定义复制构造函数,显式的使用。
例如下面这个例子:
class String{ private: int length; char * str; public: String(); String(const String & s);//复制构造函数 String(const char * s);//自定义构造函数 ~String();}String::String(const String & s)//构造函数这里的参数是给私有成员赋值的,而复制构造函数的类型是Class_name的引用{ length = s.length; str = new char [length+1]; strcpy(str,s.str);}
这个例子其实能很好的说明为什么自定义复制构造函数。
默认的复制构造函数,成员之间的复制是“值”赋值。
比如:
String s1("xxxxx");String s2("sssss");s1 = s2;
假如没有自定义复制构造函数,使用C++默认的复制构造函数的话。上述代码中的s1 = s2相当于:
s1.length = s2.length;s1.str = s2.str;
乍一看感觉十分正确,但是仔细想想其实不妥。str是一个char类型的指针,存储的是一个地址,默认的复制构造函数会直接将地址赋值,此时如果s1生命周期结束,调用了析构函数delete掉了s1.str,那么s2.str也被delete了(因为s1.str和s2.str指向的是同一块内存),如果s2在s1 delete掉后还想被程序使用,那么就会出现大麻烦,使用delete掉的内存属于未定义的行为。
其实这个地方我们只是想把s2的字面值赋值给s1罢了,因此这种情况下自定义复制构造函数很有必要了 。
类型转换与转换函数
下面来看一个例子:
class Stone{ private: int m_stone; double m_pounds; public: Stone(); Stone(double pounds);//construct for double pounds Stone(int stone,double pounds = 0);//construct for int stone and double pounds ~Stone();};Stone::Stone(double pounds){ m_pounds = pounds; m_stone = (int)pounds / 14;}
假如有如上定义。
这样,Stone(double pounds)这个构造函数就为将double类型转换为Stone类型提供了一个蓝图。
也就是说,可以这样写代码:
Stone my_stone;my_stone = 19.6;
程序将使用构造函数Stone(double pounds)来创建一个临时的Stone对象然后将19.6作为初始值,随后采用成员逐个赋值的方式将该临时对象的内容复制到my_stone中,这一过程称为隐式转换 。
(注:只有接受一个参数的构造函数才能做转换函数,或者其他参数都提供了默认值)
这种自动特性看起来很好,但是可能会导致意外的类型转换,因此,C++新增关键字explicit来显式的定义这种转换方式,即:
explicit Stone(double pounds);
Stone my_stone;my_stone = 19.6;//not valid if Stone(double) is declared as explicitmy_stone = Stone(19.6);//ok,and recommendedmy_stone = (Stone)19.6;//ok
那么,是否能进行如下转换呢?
Stone my_stone(19.6);double host = my_stone;
答案是可以的,但是不能用构造函数。
构造函数只用与将某种类型转换为类类型,要进行相反的操作,需要借助c++特殊运算符函数——转换函数。
如果定义来从Stone到double的转换函数,就可以使用下面的转换。
Stone my_stone(19.6);double host = (double)my_stone;double host = double (my_stone);
转换函数的定义如下:
operator type_name();
要注意的是,转换函数必须是类方法,转换函数没有返回类型也没有参数。
假如说要添加从Stone类型到int类型和double类型的转换,只需要:
class Stone{ private: int m_stone; double m_pounds; public: Stone(); Stone(double pounds);//construct for double pounds Stone(int stone,double pounds = 0);//construct for int stone and double pounds ~Stone(); //conversion function operator double()const; operator int() const ;};
2.动态内存管理
- C++:类的设计————构造与析构函数及其动态内存管理
- C++动态内存管理及其与C语言动态内存管理的差别
- C++——类的构造函数、析构函数与赋值函数用法浅谈
- 初识C#——构造函数与析构函数
- 申请动态内存——malloc()函数及其扩展函数
- C/C++——构造函数、复制构造函数和析构函数的执行时刻
- 【C++】基础知识—构造函数与拷贝构造函数
- 构造函数与析构函数的顺序及动态内存的申请
- C#——面向对象——继承——派生类及其构造函数
- C/C++——构造函数和析构函数
- C++设计的又一个缺陷——构造函数与析构函数名称不应该用类名
- c++ 使用动态内存分配的类需要显式复制构造函数,赋值构造函数,析构函数
- 《C++Primer》读书笔记——第12章 动态指针与内存管理
- 小满的C++学习心得(2) 说说类构造函数和动态内存管理
- 【C++】学习笔记四十七——类的构造函数和析构函数
- ANSI C动态内存分配与管理函数详解
- 第四周项目1—三角形类的构造函数(2)设计默认构造函数
- 第四周 项目一 三角形类的构造函数—设计默认构造函数
- JFinal的ORM支持(二)
- 《数据库》总结
- 学术休假---300以内的特殊数
- 15.02.07,C数据类型笔记03
- undo异常
- C++:类的设计————构造与析构函数及其动态内存管理
- POJ 3181 Dollar Dayz
- 硬盘相关
- Android中字体颜色大全-146种(完整版)
- [LeetCode]127.Word Ladder
- poj 2528 Mayor's posters 简单离散化+线段树
- 腾讯游戏数据自愈服务方案
- TCP 握手挥手详解
- Uva230