C++11特性介绍——类型与关键字相关
非静态成员的sizeof
C++ 98对非静态成员变量使用sizeof是不能通过编译的,而在C++ 11中,对于非静态成员使用sizeof完全合法的。
/*
* 非静态成员的sizeof
*/
struct People
{
public:
int hand;
static People* all;
};
void test_size_of()
{
People p;
cout << sizeof(p.hand) << endl; //C++98 通过 C++11 通过
cout << sizeof(People::all) << endl; //C++98 通过 C++11 通过
cout << sizeof(People::hand) << endl; //C++98 错误 C++11 通过
}
如果在C++ 98 中想要获取一个没有定义类实例的类成员大小的时候,可以采用下面的方式:
//C++98中获取没有类实例类的非静态成员大小的方法
cout << sizeof(((People*)0)->hand) << endl;
扩展friend的语法
回忆friend在c++中的使用,声明类的友元,友元可以无视类中成员的属性,无论成员是public,protected,或者是private,友元函数和友元类都可以访问,friend多少有点破坏了编程中的封装性,但是friend还在很多程序中被用到。
C++11 对friend做了一些改进,以使其更好用:
/*
* friend的扩展使用
*/
class FriendTest;
typedef FriendTest FT;
class Expend1
{
friend class FriendTest; //C++98 通过 C++11 通过
};
class Expend2
{
friend FriendTest; //C++98 失败 C++11 通过
};
class Expend3
{
friend FT; //C++98 失败 C++11 通过
};
//friend可以为类模板声明友元
template <typename T>
class Expend4
{
friend T;
};
Expend4<FriendTest> ef; //类型FriendTest在这里是Expend4的友元
Expend4<int> pi; //int类型模板参数,友元声明被忽略
final和override控制
final
问题:
基类A中成员函数fun声明为virtual,那么对于其派生类B,fun总是被重载的(除非被重写了),但有时,我们并不想再B中重载fun,如何解决, C++ 98明显很无奈!
解决:
C++ 11 中引入了final关键字来阻止函数继续被重写,final的作用是使派生类不可覆盖它所修饰的虚函数。
代码:
/*
* c++11中final的使用
*/
class Object
{
public:
virtual void func() = 0;
};
class Base : public Object
{
virtual void func() final;
};
class Derived : public Base
{
// void func(); //无法通过编译
};
说明:
一般final只用于派生类中,用于阻止一个接口可重载性,当然final也可以用于base类,但是这样显然是无意义的,不如直接不适用virtual岂不是更好。
override
问题:
都知道在基类声明了virtual的函数,派生类继承该函数virtual可以写也可以不写;但是在跨层继承和多重继承这样的代码就会难于阅读, 是否重载了一个接口,接口名字是否错误,都会难于检测。
解决:
C++ 11中,为了帮助程序员写复杂继承结构,引入了虚函数描述符override,如果派生类在虚函数中声明时使用了override描述符,那么该函数必须重载其基类中的同名函数。 否则代码无法编译。
代码:
struct Base1
{
virtual void Turing() = 0;
virtual void VNeumann(int g) = 0;
};
struct DerivedMid: public Base1
{
void Turing() override; //
// void VNeumann() override; //无法通过编译 参数不一样
void VNeumann(int g) override;
};
noexcept修饰符和noexcept操作符
noexcept形如其名地,表示其修饰的函数不会抛出异常。不过与throw动态异常声明不同的是,在C++11中,noexcept修饰的函数抛出了异常,编译器会直接调std::terminate()函数来终止程序的运行;
C++11中使用noexcept两种形式:
void excpt_func() noexcept;
//常量表达式的结果会被转换成bool类型的值,如果为true,表示函数不会抛出异常,反之可能抛出
void excpt_func() noexcept(常量表达式)
noexcept作为一个操作符,通常可以用于模板。比如:
template <class T>
void func() noexcept(noexcept(T())) {}
说明:
- 函数func是否是一个noexcept的函数,将由T()表达是是否会抛出异常决定。
- 这里的第二个noexcept就是一个noexcept操作。其参数是一个有可能抛出异常的表达式,其返回值为false,反之为true;
强类型枚举
声明如下:
enum class Type:char
{
General,
Light,
Medium,
Heavy
};
优点:
- 强作用域,强类型枚举成员的名称不会被输出到其父作用域空间;
- 转换限制,强类型枚举成员不可以与整型隐式地相互转换;
- 可以指定底层类型,强类型枚举默认底层类型为int,但也可以显示地指定底层类型, 如上就保存为char类型;
POD类型
POD:Plain Old Data的缩写; C++11将POD划分为两个基本概念的集合, 即:平凡的(trivial)和标准布局的(standard layout);
- 平凡的类或结构体
- 拥有默认的构造函数和析构函数;
- 拥有trivial copy constructor 和 trivial move constuctor;
- 拥有trivial assignment operator和trivial move operator;
- 不包含虚函数和虚基类;
- 标准布局的类或结构体
- 所有非静态成员有相同的访问权限(public,private, protected);
- 派生类中有非静态成员,且只有一个仅包含静态成员的基类;
- 基类有非静态成员,派生类没有非静态成员;
- 类中第一个非静态成员的类型与其基类不同;
- 没有虚函数和虚基类;
- 所有非静态数据成员均符合标准布局类型,其基类也符合标准布局;
C++11提供的判断方式如下:
//判断类型T是否是一个平凡的类型
template <typename T> struct std::is_trivial;
//是否是一个标准布局的类型
template <typename T> struct std::is_standard_layout
//是否是一个POD类型
template <typename T> struct std::is_pod
POD优点
- 字节赋值,代码中可以安全的使用memset和memcpy对POD类型进行初始化和拷贝等操作;
- 提供对C内存布局的兼容。C++ 和C可以进行相互操作,POD类型在C和C++间操作总是安全的;
- 保证静态初始化的安全有效;静态初始化能够提高性能,POD类型的对象初始化往往更加简单。
非限制联合体
- C++ 98中规定, 联合体(Union)不允许拥有非POD类型成员, 也不允许拥有静态或者引用类型的成员;
- C++ 11中, 逐渐放开了对联合体数据成员的限制, 规定任何非引用类型都可以成为联合体的成员; 这样的联合体, 即所谓的非限制联合体.
- 不过在实践中我们发现, C++ 11中, 联合体不允许存在静态的数据成员存在;
cuipf
