C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数
构造函数 ,是一种特殊的方法 。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。(摘自百度百科构造函数)。
一、最基本的构造函数
class Base
{
public:
Base(int var) : m_Var(var)
{
}
private:
int m_Var;
};
以上构造函数的执行过程:
1)传参 2)给类数据成员开辟空间 3)执行冒号语法给数据成员初始化 4)执行构造函数括号里面的内容
这里需要说明的是:冒号语法后面的内容相当于int a = 10;(初始化),而构造函数括号里面则是相当于是int a; a = 10;(赋初值)
二、拷贝构造函数
class Base
{
public:
Base(int var) : m_Var(var)
{
}
//拷贝构造函数
Base(Base &ref) : m_Var(ref.m_Var)
{
}
private:
int m_Var;
};
为什么拷贝构造函数的参数只能用引用呢?
这就要从拷贝构造函数式数码时候触发开始说起了,以下几种情况都会自动调用拷贝构造函数:
1)用一个已有的对象初始化一个新对象的时候
2)将一个对象以值传递的方式传给形参的时候
3)函数返回一个对象的时候
所以当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动的被调用来生成函数中的对象。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环直至栈溢出除了当对象传入函数的时候被隐式调用以外,拷贝构造函数在对象被函数返回的时候也同样的被调用。(摘自百度百科拷贝构造函数)。
拷贝构造函数,一般不需要自己编写,系统默认的拷贝构造函数就能抗住了,但是有些情况需要在构造的时候开辟空间,这时候就需要拷贝构造函数了,如下代码是摘自林锐博士的高质量C++编程指南一文。
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
private:
char *m_data; // 用于保存字符串
};
// String 的析构函数
String::~String(void)
{
delete [] m_data;
// 由于m_data 是内部数据类型,也可以写成 delete m_data;
} // String 的普通构造函数
String::String(const char *str)
{
if(str==NULL)
{
m_data = new char[]; // 若能加 NULL 判断则更好
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length+]; // 若能加 NULL 判断则更好
strcpy(m_data, str);
}
}
// 拷贝构造函数
String::String(const String &other)
{
int length = strlen(other.m_data);
m_data = new char[length+]; // 若能加 NULL 判断则更好
strcpy(m_data, other.m_data);
}
三、普通派生类构造函数的写法
定义派生类对象的时候,会按如下步骤执行构造操作:
1)传参 2)根据继承时的声明顺序构造基类 3)给类数据成员开辟空间 4)执行冒号语法后面的语句 5)执行构造函数函数体语句
class Base
{
public:
Base(int b) : m_b(b)
{
}
private:
int m_b;
}; class Derived : public Base
{
public:
//普通派生类构造函数的写法
Derived(int b, int d) : Base(b), m_d(d)
{
}
private:
int m_d;
};
再写一个多继承的示例:
class Base1
{
public:
Base1(int b1) : m_b1(b1)
{
}
private:
int m_b1;
}; class Base2
{
public:
Base2(int b2) : m_b2(b2)
{
}
private:
int m_b2;
}; class Derived : public Base1, public Base2
{
public:
Derived(int b1, int b2, int d) : Base1(b1), Base2(b2), m_d(d)
{ //注意冒号语法后面的顺序无所谓,创造基类是按照上面的继承声明顺序来进行的...
}
private:
int m_d;
};
四、含有虚继承的派生类构造函数的写法
为何要用到虚继承?
虚继承主要是针对多继承时,出现二义性问题而提出的。比如,如下代码就需要用到虚继承,否则的话Derived类继承时,Base类就会不明确。
虚继承构造函数的执行按照如下步骤:
1)传参 2)创建基类,注意这时候需要显示创建所有“有参构造函数”的基类,包括直接基类,间接基类。 3)给类数据成员开辟空间 4)执行冒号语法 5)执行构造函数函数体
注:你可能会疑惑,如下代码不是将Base间接基类创建了3次吗?其实不是这样的,编译器是这样处理的,当最远的派生类Derived创建了基类Base之后,其直接基类创建Base类的语句将会被忽略掉。
class Base
{
public:
Base(int b) : m_b(b)
{
}
private:
int m_b;
}; class Base1 : virtual public Base
{
public:
Base1(int b, int b1) : Base(b), m_b1(b1)
{
}
private:
int m_b1;
}; class Base2 : virtual public Base
{
public:
Base2(int b, int b2) : Base(b), m_b2(b2)
{
}
private:
int m_b2;
};
//虚继承,避免二义性
class Derived : public Base1, public Base2
{
public:
Derived(int b, int b1, int b2, int d) : Base(b), Base1(b, b1), Base2(b, b2), m_d(d)
{ //注意冒号语法后面的顺序无所谓,创造基类是按照上面的继承声明顺序来进行的...
}
private:
int m_d;
};
五、关于虚析构
虚析构一般伴随着多态而产生,多态主要方式就是用基类的指针或引用指向或引用派生类,而形成多态。
但是这样就会存在一个问题,当我们析构的时候,由于是基类的指针,就会调用的是基类的构造函数,从而造成派生内存溢出。为了解决这个问题,引入了虚析构的概念。将基类的构造函数声明为虚,从而使其在调用析构函数的时候能够准确的调用派生类的析构函数。
如下代码必须用到虚析构才能准确的析构派生类,并释放其占有内存。
class Base
{
public:
Base(int b) : m_b(b)
{
}
//虚析构,使基类指针能准确的释放所指向的派生类里面的内容
virtual ~Base()
{
}
private:
int m_b;
}; class Derived : public Base
{
public:
Derived(int b, char *pStr) : Base(b)
{
m_pStr = new char[strlen(pStr)+];
strcpy(m_pStr,pStr);
}
~Derived()
{
delete m_pStr;
m_pStr = NULL;
}
private:
char *m_pStr;
}; int main(void)
{
char *pStr = "abcdefg";
Base *b = new Derived(,pStr);
delete b; return ;
}
原文地址:http://www.cnblogs.com/nchar/p/3911427.html
C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数的更多相关文章
- 多重继承,虚继承,MI继承中虚继承中构造函数的调用情况
先来测试一些普通的多重继承.其实这个是显而易见的. 测试代码: //测试多重继承中派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include < ...
- C++继承,多重继承,虚继承的构造函数以及析构函数的调用顺序问题
#include <iostream> using namespace std; class A{ int data_a; public: A(){ data_a = ; cout < ...
- C++进阶--派生类的析构(虚析构函数,shared_ptr)
//############################################################################ /* 在多态虚基类中声明一个虚析构函数 * ...
- 记录:C++类内存分布(虚继承与虚函数)
工具:VS2013 先说一下VS环境下查看类内存分布的方法: 先选择左侧的C/C++->命令行,然后在其他选项这里写上/d1 reportAllClassLayout,它可以看到所有相关类的内存 ...
- C++:派生类的构造函数和析构函数的调用顺序
一.派生类 在C++编程中,我们在编写一个基类的派生类时,大致可以分为四步: • 吸收基类的成员:不论是数据成员还是函数成员,派生类吸收除基类的构造函数和析构函数之外的全部成员. • 改造基类函数:在 ...
- C# 基类派生类构造函数执行顺序分析
using System; namespace ConsoleApp1 { class Program{ static void Main(string[] args){ B b = new B(); ...
- 【C++ Primer 第15章】定义派生类拷贝构造函数、赋值运算符
学习资料 • 派生类的赋值运算符/赋值构造函数也必须处理它的基类成员的赋值 • C++ 基类构造函数带参数的继承方式及派生类的初始化 定义拷贝构造函数 [注意]对派生类进行拷贝构造时,如果想让基类的成 ...
- C++继承具体解释之二——派生类成员函数具体解释(函数隐藏、构造函数与兼容覆盖规则)
在这一篇文章開始之前.我先解决一个问题. 在上一篇C++继承详解之中的一个--初探继承中,我提到了在派生类中能够定义一个与基类成员函数同名的函数,这样派生类中的函数就会覆盖掉基类的成员函数. 在谭浩强 ...
- 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)
[源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...
随机推荐
- 解读sample2
说明 理解被测试代码 理解测试代码 组织关系 检查字符串比较结果的断言 说明 被测试代码文件 sample2.h.sample2.cc 测试代码文件 sample2_unittest.cc 官网上如是 ...
- XML的四种解析器原理及性能比较
转自zsq 1.DOM DOM 是用与平台和语言无关的方式表示 XML 文档的官方 W3C 标准.DOM 是以层次结构组织的节点或信息片断的集合.这个层次结构允许开发人员在树中寻找特定信息.分 ...
- jquery 定时器
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD ...
- 微信小程序简易教程
刚接触到微信小程序开发,这里做一个简单的教程: 1. 获取微信小程序的 AppID 登录 https://mp.weixin.qq.com ,就可以在网站的"设置"-"开 ...
- Android Studio 2.1.x 关联SDK API Source
问题: 看图=>,当在android studio里ctrl+鼠标左键查看例如: TextUtils.isEmpty(content);这段代码的isEmpty方法的实现的时候经常就跑到如图所示 ...
- 跟Google学习Android开发-起始篇-用碎片构建一个动态的用户界面(3)
4.3 构建一个灵活的用户界面 当设计你的应用程序要支持大范围的屏幕尺寸时,你可以在不同的布局配置中重用碎片,来根据可用的屏幕空间优化用户体验. 例如,在手持设备上,它可能是适应来在一个单窗格用户界面 ...
- SKTexture类
继承自 NSObject 符合 NSCodingNSCopyingNSObject(NSObject) 框架 /System/Library/Frameworks/SpriteKit.framewo ...
- java实现excel的导入导出(poi详解)
经过两天的研究,现在对excel导出有点心得了.我们使用的excel导出的jar包是poi这个阿帕奇公司的一个项目,后来被扩充了.是比较好用的excel导出工具. 下面来认识一下这个它吧. 我们知道要 ...
- Ubuntu apt-get 错误 -11 -system error
错误图片 上述错误是dns解析错误,不能解析域名了,所以也访问不了 解决办法,添加dns,命令如下 sudo vim /etc/resolv.conf 添加 nameserver (此外填写域名服务器 ...
- web02--jsp数据传递
1.创建一个login.jsp登陆界面 <%@ page language="java" import="java.util.*" pageEncodin ...