从零开始学C++之构造函数与析构函数(二):初始化列表(const和引用成员)、拷贝构造函数
来源:互联网 发布:java 组件开发 编辑:程序博客网 时间:2024/05/19 20:21
一、构造函数初始化列表
推荐在构造函数初始化列表中进行初始化
构造函数的执行分为两个阶段
初始化段
普通计算段
(一)、对象成员及其初始化
#include <iostream>using namespace std;class Object{public: Object(int num) : num_(num) { cout << "Object " << num_ << " ..." << endl; } ~Object() { cout << "~Object " << num_ << " ..." << endl; }private: int num_;};class Container{public: Container(int obj1 = 0, int obj2 = 0) : obj2_(obj2), obj1_(obj1) { cout << "Container ..." << endl; } ~Container() { cout << "~Container ..." << endl; }private: Object obj1_; Object obj2_;};int main(void){ Container c(10, 20); return 0;}
从输出可以看出几点,一是构造对象之前,必须先构造对象的成员;二是对象成员构造的顺序与定义时的顺序有关,跟初始化列表顺序无关;三是构造的顺序和析构的顺序相反;四是如果对象成员对应的类没有默认构造函数,那对象成员也只能在初始化列表进行初始化。再提一点,如果类是继承而来,基类没有默认构造函数的时候,基类的构造函数要在派生类构造函数初始化列表中调用。
(二)、const成员、引用成员的初始化
#include <iostream>using namespace std;// const成员的初始化只能在构造函数初始化列表中进行// 引用成员的初始化也只能在构造函数初始化列表中进行// 对象成员(对象成员所对应的类没有默认构造函数)的初始化,也只能在构造函数初始化列表中进行class Object{public: enum E_TYPE { TYPE_A = 100, TYPE_B = 200 };public: Object(int num = 0) : num_(num), kNum_(num), refNum_(num_) { //kNum_ = 100; //refNum_ = num_; cout << "Object " << num_ << " ..." << endl; } ~Object() { cout << "~Object " << num_ << " ..." << endl; } void DisplayKNum() { cout << "kNum=" << kNum_ << endl; }private: int num_; const int kNum_; int &refNum_;};int main(void){ Object obj1(10); Object obj2(20); obj1.DisplayKNum(); obj2.DisplayKNum(); cout << obj1.TYPE_A << endl; cout << obj2.TYPE_A << endl; cout << Object::TYPE_A << endl; return 0;}
因为const 变量或者引用都得在定义的时候初始化,所以const 成员和引用成员必须在初始化列表中初始化。另外,可以使用定义枚举类型来得到类作用域共有的常量。
二、拷贝构造函数
(一)、拷贝构造函数
功能:使用一个已经存在的对象来初始化一个新的同一类型的对象
声明:只有一个参数并且参数为该类对象的引用 Test::Test(const Test &other) ;
如果类中没有定义拷贝构造函数,则系统自动生成一个缺省复制构造函数,作为该类的公有成员,所做的事情也是简单的成员复制
#ifndef _TEST_H_#define _TEST_H_class Test{public: // 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的 // 默认的构造函数 Test(); explicit Test(int num); Test(const Test &other); void Display(); Test &operator=(const Test &other); ~Test();private: int num_;};#endif // _TEST_H_
#include "Test.h"#include <iostream>using namespace std;// 不带参数的构造函数称为默认构造函数Test::Test() : num_(0){ //num_ = 0; cout << "Initializing Default" << endl;}Test::Test(int num) : num_(num){ //num_ = num; cout << "Initializing " << num_ << endl;}Test::Test(const Test &other) : num_(other.num_){ //num_ = other.num_; cout << "Initializing with other " << num_ << endl;}Test::~Test(){ cout << "Destroy " << num_ << endl;}void Test::Display(){ cout << "num=" << num_ << endl;}Test &Test::operator=(const Test &other){ cout << "Test::operator=" << endl; if (this == &other) return *this; num_ = other.num_; return *this;}
#include "Test.h"int main(void){ Test t(10); //Test t2(t); // 调用拷贝构造函数 Test t2 = t; // 等价于Test t2(t); return 0;}
即调用了拷贝构造函数,destroy 的两个分别是t 和 t2。
(二)、拷贝构造函数调用的几种情况
当函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。这时要在内存新建立一个局部对象,并把实参拷贝到新的对象中。理所当然也调用拷贝构造函数。还有一点,为什么拷贝构造函数的参数需要是引用? 这是因为如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。
当函数的返回值是类对象,函数执行完成返回调用者时使用。也是要建立一个临时对象,再返回调用者。为什么不直接用要返回的局部对象呢?
因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以在处理这种情况时,编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。所谓return 对象,实际上是调用拷贝构造函数把该对象的值拷入临时对象。如果返回的是变量,处理过程类似,只是不调用构造函数。
#include "Test.h"#include <iostream>using namespace std;void TestFun(const Test t1){}void TestFun2(const Test &t1){}Test TestFun3(const Test &t1){ return t1;}const Test &TestFun4(const Test &t1){ //return const_cast<Test&>(t1); return t1;}int main(void){ Test t(10); TestFun(t); cout << "........" << endl; return 0;}
即在传参的时候调用了拷贝构造函数,函数返回时TestFun 的形参t 1生存期到了,在分割线输出之前销毁t1,最后destroy 的是 t。
将TestFun(t); 换成 TestFun2(t);
参数为引用,即没有调用拷贝构造函数。
将TestFun(t); 换成 t = TestFun3(t);
函数返回时会调用拷贝构造函数,接着调用赋值运算符,释放临时对象,最后释放t。如果没有用t 接收,不会调用operator= 而且临时对象也会马上释放。
将TestFun(t); 换成 Test t2 = TestFun3(t);
函数返回调用拷贝构造函数,但没有再次调用拷贝构造函数,而且没有释放临时对象,可以理解成临时对象改名为t2 了。
将TestFun(t); 换成 Test& t2 = TestFun3(t);
函数返回时调用拷贝构造函数,因为t2 引用着临时对象,故没有马上释放。
将TestFun(t); 换成 Test t2 = TestFun4(t);
函数传参和返回都没有调用拷贝构造函数,初始化t2 时会调用拷贝构造函数。
将TestFun(t); 换成 const Test& t2 = TestFun4(t);
函数传参和返回都没有调用构造函数,t2 是引用故也不会调用拷贝构造函数。
参考:
C++ primer 第四版
Effective C++ 3rd
C++编程规范
转载自http://blog.csdn.net/jnu_simba/article/details/9173151
- 从零开始学C++之构造函数与析构函数(二):初始化列表(const和引用成员)、拷贝构造函数
- 从零开始学C++之构造函数与析构函数(二):初始化列表(const和引用成员)、拷贝构造函数
- 从零开始学C++之构造函数与析构函数(二):初始化列表(const和引用成员)、拷贝构造函数
- 九、构造函数和析构函数(三) 初始化列表、对象成员初始化、const,引用成员初始化
- 拷贝构造函数和const成员函数
- C++中的拷贝构造函数和拷贝赋值操作符+const成员变量初始化(5)---《Effective C++》
- 从零开始学C++之构造函数与析构函数(一):构造函数、析构函数、赋值与初始化、explicit关键字
- 从零开始学C++之构造函数与析构函数(一):构造函数、析构函数、赋值与初始化、explicit关键字
- 从零开始学C++之构造函数与析构函数(一):构造函数、析构函数、赋值与初始化、explicit关键字
- C++中类的构造函数与析构函数(成员初始化列表)
- 拷贝构造函数与Const常量引用
- 从零开始学C++之构造函数与析构函数(三):深拷贝与浅拷贝、空类与空数组
- 从零开始学C++之构造函数与析构函数(三):深拷贝与浅拷贝、空类与空数组
- 从零开始学C++之构造函数与析构函数(三):深拷贝与浅拷贝、空类与空数组
- 构造函数初始化列表(C++)
- 拷贝构造函数,也可以使用成员初始化列表语法
- 从零开始学C++之继承(二):继承与构造函数、派生类到基类的转换
- 从零开始学C++之继承(二):继承与构造函数、派生类到基类的转换
- Unity内存管理——对象池(Object-Pool)
- 判断两线段是否相交求交点
- python3基础学习(3)
- PKU动态规划题集
- 常用服务器管理工具
- 从零开始学C++之构造函数与析构函数(二):初始化列表(const和引用成员)、拷贝构造函数
- Eclipse安装和配置
- jquery中CheckBox的checked状态用attr()的问题
- sdl2.0使用SDL_RenderCopyEx进行图片旋转
- 【原创】 shell一键配置squid高匿代理服务器 high-Anonymity
- NDK-Application.mk简介
- svn revert
- 1024. 科学计数法 (20)
- 第八周项目四-字符串加密