C++11中对类(class)新增的特性

default/delete 控制默认函数

在我们没有显式定义类的复制构造函数和赋值操作符的情况下,编译器会为我们生成默认的这两个函数:
默认的赋值函数以内存复制的形式完成对象的复制。
这种机制可以为我们节省很多编写复制构造函数和赋值操作符的时间,但是在某些情况下,比如我们不希望对象被复制,
在之前我们需要将复制构造函数和赋值操作符声明为private,现在可以使用delete关键字实现:

class X {
// …
X& operator=(const X&) = delete; // 禁用类的赋值操作符
X(const X&) = delete;
};

显式地使用default关键字声明使用类的默认行为,对于编译器来说明显是多余的,但是对于代码的阅读者来说,使用default显式地定义复制操作,则意味着这个复制操作就是一个普通的默认的复制操作。

override /final 强制重写/禁止重写虚函数

派生类中可以不实现基类虚函数,也可以实现,但不使用virtual关键字;

这很容易给人造成混淆,有时为了确认某个函数是否是虚函数,我们不得不追溯到基类查看;

C++11引入了两个新的标识符: override和final

override,表示函数应当重写基类中的虚函数。(用于派生类的虚函数中)

final,表示派生类不应当重写这个虚函数。(用于基类中)

struct B {
virtual void f();
virtual void g() const;
virtual void h(char);
void k(); // non-virtual
virtual void m() final;
}; struct D : B {
void f() override; // OK: 重写 B::f()
void g() override; // error: 不同的函数声明,不能重写
virtual void h(char); // 重写 B::h( char ); 可能会有警告
void k() override; // error: B::k() 不是虚函数
virtual void m(); // error: m()在基类中声明禁止重写
};

有了这对兄弟,我们的虚函数用起来更为安全,也更好阅读;

委托构造函数 Delegating constructors

在C++98中,如果你想让两个构造函数完成相似的事情,可以写两个大段代码相同的构造函数,或者是另外定义一个init()函数,让两个构造函数都调用这个init()函数。例如:

class X {
int a;
// 实现一个初始化函数
validate(int x) {
if (0<x && x<=max) a=x; else throw bad_X(x);
}
public:
// 三个构造函数都调用validate(),完成初始化工作
X(int x) { validate(x); }
X() { validate(42); }
X(string s) {
int x = lexical_cast<int>(s); validate(x);
}
// …
};

这样的实现方式重复罗嗦,并且容易出错。

在C++0x中,我们可以在定义一个构造函数时调用另外一个构造函数:

class X {
int a;
public:
X(int x) { if (0<x && x<=max) a=x; else throw bad_X(x); }
// 构造函数X()调用构造函数X(int x)
X() :X{42} { }
// 构造函数X(string s)调用构造函数X(int x)
X(string s) :X{lexical_cast<int>(s)} { }
// …
};

继承的构造函数 Inheriting constructors

C++11提供了将构造函数晋级的能力:

比如以下这个示例,基类提供一个带参数的构造函数,而派生类没有提供;

如果直接使用D1 d(6);将会报错;通过将基类构造函数晋级,派生类中会隐式声明构造函数D1(int);

需要注意的是,晋级后的基类构造函数是无法初始化派生类的成员变量的,所以如果派生类中有成员变量,

需要使用初始化列表初始化;

struct B1 {
B1(int) { }
};
struct D1 : B1 {
using B1::B1; // 隐式声明构造函数D1(int)
// 注意:在声明的时候x变量已经被初始化
int x{0};
};
void test()
{
D1 d(6); // d.x的值是0
}

类内部成员的初始化 Non-static data member initializers

在C++98标准里,只有static const声明的整型成员能在类内部初始化,并且初始化值必须是常量表达式。这些限制确保了初始化操作可以在编译时期进行。

class X {
static const int m1 = 7; // 正确
const int m2 = 7; // 错误:无static
static int m3 = 7; // 错误:无const
static const string m5 = “odd”; //错误:非整型
};

C++11的基本思想是,允许非静态(non-static)数据成员在其声明处(在其所属类内部)进行初始化。这样,在运行时,需要初始值时构造函数可以使用这个初始值。现在,我们可以这么写:

class A {
public:
int a = 7;
};
它等同于使用初始化列表:
class A {
public:
int a;
A() : a(7) {}
};

单纯从代码来看,这样只是省去了一些文字输入,但在有多个构造函数的类中,其好处就很明显了:

class A {
public:
A(): a(7), b(5), hash_algorithm(“MD5″),
s(“Constructor run”) {}
A(int a_val) :
a(a_val), b(5), hash_algorithm(“MD5″),
s(“Constructor run”)
{}
A(D d) : a(7), b(g(d)),
hash_algorithm(“MD5″), s(“Constructor run”)
{}
int a, b;
private:
// 哈希加密函数可应用于类A的所有实例
HashingFunction hash_algorithm;
std::string s; // 用以指明对象正处于生命周期内何种状态的字符串
};

可以简化为:

class A {
public:
A() {}
A(int a_val) : a(a_val) {}
A(D d) : b(g(d)) {}
int a = 7;
int b = 5;
private:
//哈希加密函数可应用于类A的所有实例
HashingFunction hash_algorithm{“MD5″};
//用以指明对象正处于生命周期内何种状态的字符串
std::string s{“Constructor run”};

多么优雅!

移动构造和移动赋值

在C++98中,我们自定义的类,会默认生成拷贝赋值操作符函数和拷贝赋值函数以及析构函数;

在C++11中,依赖于新增的move语义,默认生成的函数多了2个移动相关的:移动赋值操作符( move assignment )和移动构造函数( move constructor );

BS建议,如果你显式声明了上述 5 个函数或操作符中的任何一个,你必须考虑其余的 4 个,并且显式地定义你需要的操作,或者使用这个操作的默认行为。

一旦我们显式地指明( 声明 , 定义 , =default , 或者 =delete )了上述五个函数之中的任意一个,编译器将不会默认自动生成move操作。

一旦我们显式地指明( 声明 , 定义 , =default , 或者 =delete )了上述五个函数之中的任意一个,编译器将默认自动生成所有的拷贝操作。但是,我们应该尽量避免这种情况的发生,不要依赖于编译器的默认动作。

如果你声明了上述 5 个默认函数中的任何一个,强烈建议你显式地声明所有这 5 个默认函数。例如:

template<class T>
class Handle {
T* p;
public:
Handle(T* pp) : p{pp} {}
// 用户定义构造函数: 没有隐式的拷贝和移动操作
~Handle() { delete p; }
Handle(Handle&& h) :p{h.p}
{ h.p=nullptr; }; // transfer ownership
Handle& operator=(Handle&& h)
{ delete p; p=h.p; h.p=nullptr; } // 传递所有权
Handle(const Handle&) = delete; // 禁用拷贝构造函数
Handle& operator=(const Handle&) = delete;
// ...
};

参考

http://www.stroustrup.com/C++11FAQ.html

https://www.chenlq.net/books/cpp11-faq

Posted by: 大CC | 02SEP,2015

博客:blog.me115.com [订阅]

Github:大CC

C++11中对类(class)新增的特性的更多相关文章

  1. 关于c++11中static类对象构造函数线程安全的验证

    在c++11中,static静态类对象在执行构造函数进行初始化的过程是线程安全的,有了这个特征,我们可以自己动手轻松的实现单例类,关于如何实现线程安全的单例类,请查看c++:自己动手实现线程安全的c+ ...

  2. callable object与新增的function相关 C++11中万能的可调用类型声明std::function<...>

    在c++11中,一个callable object(可调用对象)可以是函数指针.lambda表达式.重载()的某类对象.bind包裹的某对象等等,有时需要统一管理一些这几类对象,新增的function ...

  3. 22.编写一个类A,该类创建的对象可以调用方法showA输出小写的英文字母表。然后再编写一个A类的子类B,子类B创建的对象不仅可以调用方法showA输出小写的英文字母表,而且可以调用子类新增的方法showB输出大写的英文字母表。最后编写主类C,在主类的main方法 中测试类A与类B。

    22.编写一个类A,该类创建的对象可以调用方法showA输出小写的英文字母表.然后再编写一个A类的子类B,子类B创建的对象不仅可以调用方法showA输出小写的英文字母表,而且可以调用子类新增的方法sh ...

  4. 一起学习c++11——c++11中的新增的容器

    c++11新增的容器1:array array最早是在boost中出现:http://www.boost.org/doc/libs/1_61_0/doc/html/array.html 当时的初衷是希 ...

  5. C++中的类继承(2)派生类的默认成员函数

    在继承关系里面, 在派生类中如果没有显示定义这六个成员 函数, 编译系统则会默认合成这六个默认的成员函数. 构造函数. 调用关系先看一段代码: class Base { public : Base() ...

  6. C++11 中function和bind以及lambda 表达式的用法

    关于std::function 的用法:  其实就可以理解成函数指针 1. 保存自由函数 void printA(int a) { cout<<a<<endl; } std:: ...

  7. C++ 11中几个我比较喜欢的语法(三)

    随着Vsisual Studio 2013 RC版的放出,之前承诺的对C++ 11语法支持已经全部完成,本文是C++ 11中我喜欢的语法系列的最后一部分(一),(二). 非静态成员直接初始化 在C++ ...

  8. 对C++11中的`移动语义`与`右值引用`的介绍与讨论

    本文主要介绍了C++11中的移动语义与右值引用, 并且对其中的一些坑做了深入的讨论. 在正式介绍这部分内容之前, 我们先介绍一下rule of three/five原则, 与copy-and-swap ...

  9. C++ primer plus读书笔记——第11章 使用类

    第11章 使用类 1. 运算符重载是一种形式的C++多态. 2. 不要返回指向局部变量或临时对象的引用.函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据. 3. 运算符重载的格式如下: ...

随机推荐

  1. JS写的CRC16校验算法

    var CRC = {}; CRC.CRC16 = function (data) { var len = data.length; if (len > 0) { var crc = 0xFFF ...

  2. WordPress基础:极简安装教程

    1.下载WordPress 2.将解压后的文件夹,放到网站根目录,并重命名为你喜欢的目录如:w, 3.重命名文件wp-config-sample.php 为 wp-config.php,并进行配置 4 ...

  3. java 使用 poi 解析excel

    背景: web应用经常需要上传文件,有时候需要解析出excel中的数据,如果excel的格式没有问题,那就可以直接解析数据入库. 工具选择: 目前jxl和poi可以解析excel,jxl很早就停止维护 ...

  4. lsof 命令 系统管理 系统监控

    lsof(list open files)是一个列出当前系统打开文件的工具.在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件. 在终端下输入ls ...

  5. php闭包实现函数的自调用,也是递归

    php的闭包可能不常用,但是在某些场合之下还是可以考虑用php的闭包来实现某些功能的,比如递归,这里讲一下用php的闭包实现递归 //php闭包实现函数的自调用,也就是实现递归 function cl ...

  6. 由SecureCRT引发的思考和学习

    由SecureCRT引发的思考和学习 http://mp.weixin.qq.com/s?__biz=MzAxOTAzMDEwMA==&mid=2652500597&idx=1& ...

  7. Hausdorff distance

    微分动力系统原理 这本书里有介绍 Hausdorff距离是描述两组点集之间相似程度的一种量度,它是两个点集之间距离的一种定义形式:假设有两组集合A={a1,…,ap},B={b1,…,bq},则这两个 ...

  8. dotnetnuke peek. glance.

    /**** 15:59:39.05 ***/ use dotnetnuke to create websites: 1. install 2. create webpage template 3. c ...

  9. 简单的bat命令去获取wsdl的源码

    @echo off cd C:\Program Files\Java\jdk1..0_45\bin for /R "D:\wqcCode\company\emtproj\02 Require ...

  10. struts2数据校验与国际化

    数据校验: Action里的validate()方法能校验action类所有的方法,如果有错,如:addFieldError,会自动返回到workflow校验拦截器不向下继续进行,不用return i ...