C++构造/析构/赋值函数
在编写C++程序的时候,我们会为特定某一类对象申明类类型,几乎我们申明的每一个class都会有一个或多个构造函数、一个析构函数、一个赋值运算符重载=、以及拷贝构造函数。这些函数控制着类对象的基础操作,确保新定义的对象的初始化、完成对象撤销时的清理工作、赋予对象新值。如果这些函数的操作出错,则会导致严重的后果,所以确保这些函数的操作行为正常是非常重要的。
一、编译器默认生成的函数
如果我们编写一个空类,编译器会为我们默认生成构造函数、析构函数、赋值运算符、拷贝构造函数。
例如当我们定义
class Empty{ };
就好像我们写下了如下代码(红色是编译器默认生成)
class Empty{
public:
Empty(){....} //默认构造函数
Empty(const Empty &rhs){....} //默认拷贝构造函数
~Empty(){....} //默认析构构造函数
Empty& operator=(const Empty &rhs){....} //赋值运算符
Empty* operator&(){...} //取地址运算符
const Empty* operator&() const{...} //取地址运算法的const版本
};
1. 说明:(1)这些函数只有在被调用的时候,才会被编译器创建出来;
(2)四个函数都public且inline的;
(3)如果显示的定义了其中某一个函数,那么编译器就不会生成其对应的默认的版本;
(4)自定义的拷贝构造函数不仅会覆盖默认的拷贝构造函数,同时也会覆盖默认的构造函数,下面的函数class构造函数,不能通过编译
#include <iostream>
using namespace std;
class Empty
{
public:
Empty(const Empty &Copy){};
};
int main(int argc, char** argv)
{
Empty a;
return ;
}
2. 实例:
下面的代码会让编译器创建默认的构造函数 Empty e1; //默认构造函数
Empty e2(e1);//拷贝构造函数
e2 = e1;//赋值运算符
#include <iostream>
using namespace std; class Empty{ public:
Empty(){cout << "create" << endl;}
Empty(const Empty &Copy){ cout << "copy" << endl;}
Empty& operator=(const Empty &Assig){cout << "assign=" <<
endl;}
Empty* operator&(){cout << "&" << endl;}
const Empty* operator&() const {cout << "&1" << endl;}
~Empty(){cout << "delete" << endl;}
};
int main()
{
Empty *e = new Empty(); // create
delete e; //delete
Empty e0; //create
const Empty e1; //create
Empty e2(e1); //copy
Empty e3; //create
e3 = e1;//assign=
cout << &e0 << endl;//& 0x602080
const Empty *p = &e1;//&1
cout << p << endl; //0x602080
return ;
}
//e0,e1,e2,e3对象被撤销时候删除
delete
delete
delete
delete
二、构造函数
1. 构造函数的作用
构造函数是特殊的成员函数,用来在创建对象时完成对对象属性的一些初始化等操作, 当创建对象时, 对象会自动调用它的构造函数。
2. 默认构造函数
正如第一部分所述,如果没有为一个类显示定义任何构造函数、编译器将自动为这个类生成默认构造函数。默认构造函数将依据变量初始化的规则初始化类中的所有成员:
(1)对于具有类类型的成员,会调用该成员所属类自身的默认构造函数实现初始化;
(2)内置类型成员的初值依赖于对象如何定义,如果对象在全局作用域中定义或定义为静态局部对象,则这些成员将被初始化为0。如果对象在局部作用域中定义,则这些成员没有初始化;
(3)默认构造函数一般适用于仅包含类类型的成员的类;
(4)由于默认构造函数不会初始化内置类型的成员,所以必须显示定义类的构造函数。
#include <iostream>
using namespace std;
class Empty
{
public:
int a;
string s;
}; int main(int argc, char** argv)
{
Empty a;
cout << a.a << endl;//输出a的值随机
cout << a.s.size() << endl;//s是类类型被初始化为空串
}
3. 构造函数的特点
(1)在对象被创建时自动执行;
(2)构造函数的函数名与类名相同;
(3)没有返回值类型、也没有返回值;
(4)构造函数不能被显式调用;
4. 重载构造函数
可以为一个类申明的构造函数的数量没有限制,只要每个构造函数的形参表示唯一的。定义类对象的时候,实参指定使用哪个构造函数。比如我们定义类Sales_item,它的构造函数有三个,在定义类的新对象时,可以使 用这些构造函数中的任意 一个。
Class Sales_item{
public:
Sales_item(const std::string&);
Sales_item(std::istream&);
Sales_item();
};
int main()
{
Sales_item empty;//使用缺省的无参构造函数
Sales_item Primer_3rd_Ed("0-201-82470-1");
Sales_item Primer_4th_ed(cin);
return ;
}
5. 构造函数自动执行
只要创建对应类类型的一个对象,编译器就运行一个构造函数。
Sales_item Primer_2nd("0-201-54848-8");//运行带string参数的构造函数
Sales_item *p = new Sales_item();//通过默认构造函数初始化该对象
6. 构造函数初始化列表
对象中的一些数据成员除了在构造函数体中进行初始化外,还可以通过构造函数初始化列表进行初始化,构造函数初始化列表只在构造函数的定义中而不是声明中指定。从概念上将讲,可以认为构造函数分两个阶段执 行:(1)初始化阶段;(2)普通计算阶段,计算阶段由构造函数函数体中的所有语句组成;(3)构造函数就是按照成员定义的次序初始化成员的次序。
不管成员是否在构造函数初始化列表中显式初始化,类类型的数据成员总是在初始化阶段初始化。初始化阶段发生在计算阶段开始之前。
Sales_item::Sales_item(const string &book): isbn(book), units_sold(), revenue(0.0){}
说明:对于const类型成员、引用类型的成员变量都必须在构造函数初始化列表中进行初始化,例如下面的代码就是错误的,必须在初始化列表中对类成员变量进行初始化。
class ConstRef{
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
ConstRef::ConstRef(int ii)
{
//赋值
i = ii;
ci = ii; //错误,不能对const成员赋值
ri = i;//不能对引用变量赋值
}
记住,可以初始化const对象或引用类型的对象,但不能对它们赋值。在开始执行构造函数体之前,要完成初始化。初始化const或引用类型的唯一机会是在构造函数初始化列表中。编写以上构造函数的正确方式为
ConstRef::ConstRef(int ii):i(ii), ci(i), ri(ii)
二、析构函数
构造函数的一个作用是自动获取资源。例如,构造函数可以分配一个缓冲区或打开一个文件,在构造函数中分配了资源之后,需要一个对应操作自动回收或释放资源。析构函数就是这样一个特殊函数,它可以完成所需资 源的回收,作为类的构造函数的补充。
1.何时调用析构函数
a.删除指向动态分配对象的指针
b.实际对象(而不是对象的引用)超出作用域时
c.撤销一个容器(不管是标准库容器还是内置数组)时,即超出容器的作用范围时
2.缺省析构函数
a.编译器总会为我们合成一个析构函数,其按照对象创建时的逆序撤销每个非static成员,因此,它按照成员在类中申明的次序的逆序撤销成员。
b.缺省的析构函数并不删除指针成员指向的对象
c.析构函数与赋值操作符和复制构造函数之间的一个重要区别是,及时我们自己编写了自己的析构函数,缺省的析构函数任然运行
d.对于类类型的对象,合成析构函数调用其析构函数完成对象的释放;对于内置类型的对象,合成析构函数则不做什么操作
3.何时编写显式析构函数
许多类不需要显式析构函数,尤其具有构造函数的类不一定需要定义自己的析构函数。仅在有些仅在有些工作需要析构函数完成时,才需要析构函数(显式的)。析构函数并不仅限于用来释放资源,一般而言,析构函数可以执行任意操作,该操作是类设计者希望该类对象在使用完毕后执行的。
C++构造/析构/赋值函数的更多相关文章
- EffectiveC++ 第2章 构造/析构/赋值运算
我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的"可能比较准确"的「翻译」. Chapter 2 构造 / 析构 / 赋值 条款 05:了解C++ ...
- 《Effective C++》第2章 构造/析构/赋值运算(2)-读书笔记
章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...
- 《Effective C++》第2章 构造/析构/赋值运算(1)-读书笔记
章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...
- Effective C++笔记:构造/析构/赋值运算
条款05:了解C++默默编写并调用哪些函数 默认构造函数.拷贝构造函数.拷贝赋值函数.析构函数构成了一个类的脊梁,只有良好的处理这些函数的定义才能保证类的设计良好性. 当我们没有人为的定义上面的几个函 ...
- Effective C++ 笔记二 构造/析构/赋值运算
条款05:了解C++默默编写并调用哪些函数 编译器默认声明一个default构造函数.一个copy构造函数.一个copy assignment操作符和一个析构函数.这些函数都是public且inlin ...
- Effective C++ —— 构造/析构/赋值运算(二)
条款05 : 了解C++默默编写并调用哪些函数 编译器可以暗自为class创建default构造函数.copy构造函数.copy assignment操作符,以及析构函数. 1. default构造函 ...
- Effective C++笔记(二):构造/析构/赋值运算
参考:http://www.cnblogs.com/ronny/p/3740926.html 条款05:了解C++默默编写并调用哪些函数 如果自定义一个空类的话,会自动生成默认构造函数.拷贝构造函数. ...
- 【Effective C++】构造/析构/赋值运算
条款05:了解C++默默编写并调用哪些函数 默认构造函数.拷贝构造函数.拷贝赋值函数.析构函数构成了一个类的脊梁,只有良好的处理这些函数的定义才能保证类的设计良好性. 当我们没有人为的定义上面的几个函 ...
- EC++学习笔记(二) 构造/析构/赋值
条款05:了解c++默默编写并调用了哪些函数 编译器可以暗自为 class 创建default构造函数,copy构造函数,copy assignment操作和析构函数所有这些函数都是 public 并 ...
随机推荐
- class DelegatingFilterProxy
/** * Proxy for a standard Servlet Filter, delegating to a Spring-managed bean that * implements the ...
- jsp 是什么 ,jsp 隐式对象
google 搜索 java server page http://www.oracle.com/technetwork/java/javaee/jsp/index.html http://docs. ...
- js url图片转bese64
function convertImgToDataURLviaCanvas(url, callback, outputFormat){ var img = new Image(); img.cross ...
- 【转】Android开发实践:自定义带消息循环(Looper)的工作线程
http://ticktick.blog.51cto.com/823160/1565272 上一篇文章提到了Android系统的UI线程是一种带消息循环(Looper)机制的线程,同时Android也 ...
- c# 使用hook来监控鼠标键盘事件的示例代码
如果这个程序在10几年前,QQ刚刚兴起的时候,有了这个代码,就可实现盗号了. 当然使用钩子我们更多的是实现"全局快捷键"的需求. 比如 程序最小化隐藏后要"某快捷键&qu ...
- 使用Machin公式计算
使用Machin公式计算,并使用百亿进制+末项位数控制,这里可算出数万位(比最简PI快80倍),源代码约40行,在本网页中. 计算公式 PI=16arctg(1/5)-4arctg(1/239),其中 ...
- 关于Javascript的内存泄漏问题的整理稿
写了好长时间javascript小功能模块,从来没有关注过内存泄漏问题.记得以前写C++程序的时候,内存泄漏是个严重的问题,我想是时候关注一下了.网上找了篇文章,Mark一下.原文地址:http:// ...
- 【JSP】JSTL使用core标签总结(不断更新中)
使用core标签 在页面中使用taglib指令指定标签URI和prefix.如: <%@ taglib uri="http://java.sun.com/jsp/jstl/core&q ...
- Java Web模块——验证码模块
一.什么是验证码及它的作用 验 证码为全自动区分计算机和人类的图灵测试的缩写,是一种区分用户是计算机的公共全自动程序,这个问题可以由计算机生成并评判,但是必须只有人类才能解答. 可以防止恶意破解密码. ...
- png图片那点事
PNG图片格式现在包含三种类型: 1.PNG8 256色PNG的别名 2.PNG24 全色PNG的别名 3.PNG32 全色PNG的别名 基本上PNG32就是PNG24,但 ...