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. [SoapUI] EndPoint不需要在配置文件中设置不同环境的值,SoapUI自带此参数的设置

    在Environments里面设置就好了

  2. qt学习(一) qt安装

    QT5现在安装已经方便了许多 相比QT4 可以一路点击到底 无需额外的太多的操作. http://download.qt.io/official_releases/ 下载 1 windows下可以选择 ...

  3. jsoncpp学习

    // MyJsonTest.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <fstream> #includ ...

  4. centos下修改文件后如何保存退出

    centos下修改文件后如何保存退出 保存命令 按ESC键 跳到命令模式,然后: :w 保存文件但不退出vi :w file 将修改另外保存到file中,不退出vi :w! 强制保存,不推出vi :w ...

  5. apicloud 和 微信小程序,你会用哪 个?

    微信 小程序开始火了,app跨平台的革命再次高涨,不得不说,不用再担心android和ios双版本开发成本,及h5的开发 和apicloud一样,不需要关注平台问题,只需要关注前端js.css就能大a ...

  6. Windows sql语句正则匹配导出数据到本地 The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

    尝试使用 into outfile导出数据的时候出现错误: The MySQL server is running with the --secure-file-priv option so it c ...

  7. JSTL 标签库<转>

    http://elf8848.iteye.com/blog/245559 JSTL标签库,是日常开发经常使用的,也是众多标签中性能最好的.把常用的内容,放在这里备份一份,随用随查.尽量做到不用查,就可 ...

  8. windows下 git+tortoiseGit的使用【转】

    一定要自己写出来才能牢记,所以我来写一下 git确实比svn好用的多了,最起码只有一个文件夹用来标记版本信息比svn所有文件夹下都要放一个文件夹来标记版本信息先进多了,不然你不想要版本管理这些文件的时 ...

  9. 深刻理解Java编程的7个例子

    1. 阅读下列代码回答问题(第一个Java程序,理解PATH和CLASSPATH,学会使用javac和java命令) package cn.edu.uibe;    public class Hell ...

  10. Android-画板

    在上一篇博客,Android-图像原理/绘制原理,讲解到绘图原理中,画布 + 画笔