构造函数 ,是一种特殊的方法 。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与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++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数的更多相关文章

  1. 多重继承,虚继承,MI继承中虚继承中构造函数的调用情况

    先来测试一些普通的多重继承.其实这个是显而易见的. 测试代码: //测试多重继承中派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include < ...

  2. C++继承,多重继承,虚继承的构造函数以及析构函数的调用顺序问题

    #include <iostream> using namespace std; class A{ int data_a; public: A(){ data_a = ; cout < ...

  3. C++进阶--派生类的析构(虚析构函数,shared_ptr)

    //############################################################################ /* 在多态虚基类中声明一个虚析构函数 * ...

  4. 记录:C++类内存分布(虚继承与虚函数)

    工具:VS2013 先说一下VS环境下查看类内存分布的方法: 先选择左侧的C/C++->命令行,然后在其他选项这里写上/d1 reportAllClassLayout,它可以看到所有相关类的内存 ...

  5. C++:派生类的构造函数和析构函数的调用顺序

    一.派生类 在C++编程中,我们在编写一个基类的派生类时,大致可以分为四步: • 吸收基类的成员:不论是数据成员还是函数成员,派生类吸收除基类的构造函数和析构函数之外的全部成员. • 改造基类函数:在 ...

  6. C# 基类派生类构造函数执行顺序分析

    using System; namespace ConsoleApp1 { class Program{ static void Main(string[] args){ B b = new B(); ...

  7. 【C++ Primer 第15章】定义派生类拷贝构造函数、赋值运算符

    学习资料 • 派生类的赋值运算符/赋值构造函数也必须处理它的基类成员的赋值 • C++ 基类构造函数带参数的继承方式及派生类的初始化 定义拷贝构造函数 [注意]对派生类进行拷贝构造时,如果想让基类的成 ...

  8. C++继承具体解释之二——派生类成员函数具体解释(函数隐藏、构造函数与兼容覆盖规则)

    在这一篇文章開始之前.我先解决一个问题. 在上一篇C++继承详解之中的一个--初探继承中,我提到了在派生类中能够定义一个与基类成员函数同名的函数,这样派生类中的函数就会覆盖掉基类的成员函数. 在谭浩强 ...

  9. 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成员)

    [源码下载] 不可或缺 Windows Native (21) - C++: 继承, 组合, 派生类的构造函数和析构函数, 基类与派生类的转换, 子对象的实例化, 基类成员的隐藏(派生类成员覆盖基类成 ...

随机推荐

  1. php 环境搭建注意事项

    安装php 环境,最后安装集成环境,因为 apache+php 互联不是很容易. 这里主要是两个工具集成工具(wamp ,AppServ),其实这两个集成环境都包括(apache+mysql+php) ...

  2. [Design Pattern] Front Controller Pattern 简单案例

    Front Controller Pattern, 即前端控制器模式,用于集中化用户请求,使得所有请求都经过同一个前端控制器处理,处理内容有身份验证.权限验证.记录和追踪请求等,处理后再交由分发器把请 ...

  3. L - Cat VS Dog - HDU 3829(最大独立集)

    题意:有P个孩子,有的孩子喜欢猫不喜欢狗,有的喜欢狗不喜欢猫(喜欢的和不喜欢的一定是相相对立的动物),动物园有N只猫,M只狗,每个孩子都有喜欢的猫讨厌的狗(或者喜欢的狗讨厌的猫),现在动物园要送走一批 ...

  4. Java程序员的日常—— 垃圾回收中引用类型的作用

    在Java里面,是不需要太过于关乎垃圾回收,但是这并不意味着开发者可以不了解垃圾回收的机制,况且在java中内存泄露也是家常便饭的事情.因此了解垃圾回收的相关知识就显得很重要了. 引用,在垃圾回收中是 ...

  5. Oracle 数据库基本操作——用户管理与文件管理

    目录: 1.初始状态 2.登录数据库 3.创建表空间 1)概念 2) 基本表空间 3)表空间管理 4.创建新用户 5.删除用户 6.用户的授权 1)定义 2)授予权限的方法 3)权限分类 4)授权注意 ...

  6. 从VSS到SVN再到Git 记Git的基本操作

    Source code control 一直是软件开发过程中重要的环节,从最初的纯文件备份,到使用工具进行管理.Source code control 工具的作用也不仅仅只是单纯的对同一个版本进行管理 ...

  7. [Redux] Implementing Store from Scratch

    Learn how to build a reasonable approximation of the Redux Store in 20 lines. No magic! const counte ...

  8. Java基础知识强化70:正则表达式之引入案例(QQ号码校验)

    1. 校验QQ号码的案例,如下: package cn.itcast_01; import java.util.Scanner; /* * 校验qq号码. * 1:要求必须是5-15位数字 * 2:0 ...

  9. 修改用户的home路径

    1.直接修改/etc/passwd文件 2.usermod -d /hadoop -u 1531 附:usermod详细参数 语 法:usermod [-LU][-c <备注>][-d & ...

  10. Linux shell入门基础(三)

    三.输入输出重定向及管道 01.过滤器   Linux过滤器分三种:     1.过滤器(重定向只对过滤器有作用) #gzip a(将a作为输入源,涉及到输入输出)     2.编辑器     3.交 ...