1. 继承构造函数

派生类如果要使用基类的成员函数,可以通过using声明来完成。

 #include <iostream>
using namespace std; class Base {
public:
void func(double f) { cout << "Base: " << f << endl; }
}; class Derived: Base {
public:
using Base::func;
void func(int i) { cout << "Derived: " << i << endl; }
}; int main()
{
Base b;
b.func(4.5); // Base: 4.5
Derived d;
d.func(4.5); // Base: 4.5
d.func(); // Derived: 3
}

如上代码,使用了using声明,声明派生类Derived也使用基类版本的函数func。在C++11中,这个想法被扩展到构造函数上。

class A {
public:
A(int i) {}
A(double d, int i) {}
A(float f, int i, const char* c) {}
}; class B: A {
public:
using A::A; // 继承构造函数
virtual void ExtraInterface() {}
int d=; // 默认值
}; int main()
{
//B b; // 编译报错,不会生成默认的构造函数 B()
B b1();
B b2(2.3, );
B b3(2.3, , "abc");
}

这样使用using A::A 来声明把基类中的构造函数悉数继承到派生类B中。using继承有以下规则:

  • 继承构造函数只会初始化基类中的成员变量
  • 不能继承基类中的私有构造函数以及私有成员函数
  • 派生类如果是从基类中虚继承的,那么不能在派生类中声明继承构造函数
  • 一旦使用了继承构造函数,编译器就不会为派生类生成默认构造函数

2. 委派构造函数

如上例,我们在 Info(int) 和 Info(char)的初始化列表的位置调用了“基准版本”的构造函数Info() 。在初始化列表中调用“基准版本”的构造函数为委派构造函数,而被调用的“基准版本”则为目标构造函数。

所谓委派构造,就是指委派函数将构造的任务委派给了目标构造函数来完成的一种类构造方式。委派构造函数不能有初始化列表造成的,即构造函数不能同时“委派”和使用初始化列表,如果委派构造函数需要给成员变量赋初值,只能放在函数体中。

由于初始化列表的初始化方式总是先于构造函数完成的,这样上述初始化显得不够满意,可以改一下使得委派构造函数依然可以在初始化列表中初始化所有成员。

2.1 委派构造函数的应用

应用1:使用构造模板函数产生目标构造函数。

#include <list>
#include <vector>
#include <deque>
using namespace std; class TDConstructed {
template<class T> TDConstructed(T first, T last) : l(first, last) {}
list<int> l; public:
TDConstructed(vector<short> &v) : TDConstructed(v.begin(), v.end()) {}
TDConstructed(deque<int> &d) : TDConstructed(d.begin(), d.end()) {}
};

如上例子,定义了一个构造函数模板。而通过两个委派构造函数的委托,构造函数模板会被实例化。这样TDConstructed类可以很容易的接受多种容器对其进行初始化。委托构造使得构造函数的泛型编程也成为了一种可能。

应用2:异常处理,如果在委派构造函数中使用try的话,那么从目标构造函数产生的异常,都可以在委派构造函数中被捕捉到。

class DCExcept {
public:
DCExcept(double d) try : DCExcept(, d) {
cout << "Run the body." << endl;
// 其他初始化
}
catch (...) {
cout << "caught exception." << endl;
} private:
DCExcept(int i, int d) {
cout << "going to throw!" << endl;
throw ;
} int type;
double data;
}; int main()
{
DCExcept a(1.2);
}

上述代码中,目标构造函数DCExcept(int i, int d)抛出了一个异常,并在委派构造函数DCExcept(double d)进行捕捉。

3. 右值引用

一个左值表达式代表的是对象本身,而右值表达式代表的是对象的值;变量也是左值。可以这么理解:对于一个表达式,凡是对其取地址(&)操作可以成功的都是左值,否则就是右值。

为了支持移动操作(包括移动构造函数和移动赋值函数),C++才引入了一种新的引用类型——右值引用。右值引用的形式为:类型 && a= 被引用的对象 ,右值引用只能绑定到右值

3.1 C++拷贝构造函数问题

拷贝构造函数中为指针成员分配新的内存再进行内容拷贝的做法在C++编程中几乎被视为不可违背的。不过这样能带来一些问题。

从运行结果来看,这个示例中调用了两次拷贝构造函数构造了两个临时对象,一次是在instance函数返回时,一次在对t的初始化。其实这两个临时对象并没有什么意义,构造完了马上就析构了。如果拷贝的内存非常大,就影响了效率,为了解决这个问题,C++11引用了右值引用这个类型。

3.2 移动构造函数

有没有可能将 在工厂函数当中所构造对象的成员变量(m_p)所指向的那块内存“偷”过来,而不是重新开辟一块内存将之前的内容复制过来呢? 这就是移动构造函数设计的思想。所谓移动构造函数,大家从名字上应该可以猜到:它应该就是一种构造函数,只不过它接受的参数是一个本类对象的右值引用,对于本例,移动构造函数的定义如下:
Test(Test &&t): m_p(t.m_p)
{
cout << "move constructor" << endl;
t.m_p = nullptr;
}

3.3 std::move

现在我们来看另外一种场景,在下面的情况下,我们知道在Test t2(t1)处会调用拷贝构造函数(t1是左值,因此不会调用移动构造函数),那么有没有一种办法在此处也调用移动构造函数而不是拷贝构造函数呢?

int main()
{
Test t1;
// ......
Test t2(t1);// ......
}
答案是肯定的,C++11标准中给我们提供了std::move来解决这个问题,如下,只需将Test t2(t1)换成下面的语句即可:Test t2(std::move(t1))
这个std::move的作用就是将左值转换为右值,以便调用移动构造函数。这里有一点特别需要注意的是,在Test t2(std::move(t1))语句后,不能再对t1进行操作了,因为在移动构造函数中,已经将t1的成员变量m_p置为nullptr了。

2. C++11 构造函数相关的更多相关文章

  1. C#构造函数相关主题

    using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...

  2. C++11—lambda函数

    [1]lambda表达式语法定义 lambda表达式的语法定义如下: [capture] (parameters) mutable ->return-type {statement}; (1) ...

  3. 21 , CSS 构造模型

    1. div 2. 边距 3. 边框 4. 定位 5. 浮动 1 21.1  div 部分(division)---<div>元素,经常以 div 形式引用---是 XHTML 元素,用于 ...

  4. C++构造函数知识点整理(C++11标准)

    引言 构造函数是c++中的一个比较难的语法知识点.编程实践中,由于在很多情况下可以不显示定义构造函数,或者,虽然定义构造函数的方式并不十分适当,但是程序也能正常运行,故而并不是特别引起开发者的重视. ...

  5. [ACM训练] 算法初级 之 数据结构 之 栈stack+队列queue (基础+进阶+POJ 1338+2442+1442)

    再次面对像栈和队列这样的相当基础的数据结构的学习,应该从多个方面,多维度去学习. 首先,这两个数据结构都是比较常用的,在标准库中都有对应的结构能够直接使用,所以第一个阶段应该是先学习直接来使用,下一个 ...

  6. [java] 深入理解内部类: inner-classes

    [java] 深入理解内部类: inner-classes // */ // ]]>   [java] 深入理解内部类: inner-classes Table of Contents 1 简介 ...

  7. C++多线程环境下的构造函数

    多线程的环境里,我们总不可避免要使用锁.于是一个常见的场景就是: class ObjectWithLock { private: std::mutex mtx_; SomeResType shared ...

  8. jQuery extend方法使用及实现

    一.jQuery extend方法介绍 jQuery的API手册中,extend方法挂载在jQuery和jQuery.fn两个不同对象上方法,但在jQuery内部代码实现的是相同的,只是功能却不太一样 ...

  9. 深入学习JavaScript对象

    JavaScript中,除了五种原始类型(即数字,字符串,布尔值,null,undefined)之外的都是对象了,所以,不把对象学明白怎么继续往下学习呢? 一.概述 对象是一种复合值,它将很多值(原始 ...

随机推荐

  1. Rotate image and fit show use canvas

    Description In the field of image processing, We always to show image after modified the image raw d ...

  2. 邮槽 匿名管道 命名管道 剪贴板 进程通讯 转自http://www.cnblogs.com/kzloser/archive/2012/11/04/2753367.html#

    邮槽 通信流程: 服务器 客户端 注意: 邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输 邮槽可以实现一对多的单向通信,我们可以利用这个特点编写一个网络会议通知系统,而且实现这一的系 ...

  3. gsm

    libosmocore Osmocom-BB wireshark 拦截一个短信内容

  4. .net core利用MySqlBulkLoader大数据批量导入MySQL

    最近用core写了一个数据迁移小工具,从SQLServer读取数据,加工后导入MySQL,由于数据量太过庞大,数据表都过百万,常用的dapper已经无法满足.三大数据库都有自己的大数据批量导入数据的方 ...

  5. spark standalone集群部署 实践记录

    本文记录了一次搭建spark-standalone模式集群的过程,我准备了3个虚拟机服务器,三个centos系统的虚拟机. 环境准备: -每台上安装java1.8 -以及scala2.11.x (x代 ...

  6. windows平台下spark-shell配置

    一.下载安装spark,http://spark.apache.org/,选择合适版本后下载到本地,解压,bin目录下spark-shell文件就是spark命令行交互的入口. 二.下载安装windo ...

  7. IntelliJ IDEA 2016.1.3(64) license server 与汉化

    license server:http://idea.iteblog.com/key.php 汉化:将resources_cn.jar 复制到安装IDEA安装目录下的lib文件夹中.重新打开即可. r ...

  8. lambda distinct

    public ActionResult Index() { IList<RegisterModel> regList = new List<RegisterModel>() { ...

  9. Android-Camera+SurfaceView

    Camera相机是属于硬件,每台设备的Camera硬件配置的参数都是不一样的,Camera通常是用来拍照,扫描二维码等等 AndroidManifest.xml配置Camera需要的权限: <! ...

  10. PipelineDB On Kafka

    PipelineDB 安装yum install https://s3-us-west-2.amazonaws.com/download.pipelinedb.com/pipelinedb-0.9.8 ...