您的位置:首页 > 编程语言 > C语言/C++

C++11特性-<<深入理解C++11>>读书笔记

2016-09-21 20:51 483 查看

新增关键字

alignas

struct alignas(32) ColorVector
{
double r;
double g;
double b;
double a;
} //没有alignas关键字的话,对齐到8位,加上的话,对齐到32位,能提升效率。


对齐的数据在读写上会有性能上的优势。比如频繁使用的数据如果与处理器的高速缓存器大小对齐,有可能提高缓存的性能。而数据不对齐可能造成一些不良的后果,比较严重的当属应用程序退出。典型的,如在有的平台,硬件将无法读取不按字节对齐的某些类型数据,硬件会抛出异常来终止程序。


- alignof

表示一个定义完整的自定义类型或者内置类型或者变量,返回的值是一个std::Size_t类型的整型常量。


alignof(int);//值是4,表示对齐的位数


decltype

推导类型,比较典型的是跟typedef和using的合用。在一些常量,基本类型,运算符,操作符等都已经被定义好的情况下,类型可以按照规则被导出。而用using,就可以为这些类型取名。

using nullptr_t = decltype(nullptr);

vector<int> vec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin() ; i < vec.end(); i++)
{
//do something;
}


auto(重新定义)

auto声明变量的类型必须由编译器在编译时期推导而得。声明时需要赋初值

auto z;
z = 1; //编译报错 应该是auto z = 1;


用auto的优势在于拥有初始化表达式的复杂类型变量声明时简化代码。还可以免除程序员在一些类型声明时的麻烦或避免一些在类型声明时的错误,另一个优势在于其“自适应”性能在一定程度上支持泛型编程。


template<typename T1, typename T2>
double Sum(T1 &t1, T2 &t2){
auto s = t1 + t2;
return s;
}


但auto不是万能的,以下几种情况不能编译

void fun(auto x = 1) {} //auto函数参数,无法通过编译
struct str{
auto var = 10; //auto非静态成员变量,无法通过编译
}
int main(){
char x[3];
auto y = x;
auto z[3] = x; //auto数组,无法通过编译
vector<auto> v = {1}; //auto模板参数(实例化时),无法通过编译
}


static_assert

一般的断言是在程序运行起来的时候起作用,如果想在编译期做断言,则需要静态断言。它有两个参数:第一个是断言表达式,另一个是报错信息。

template <typename T, typename U> int bit_copy(T &a, U&b)
{
static_assert(sizeof(b) == sizeof(a), "error");
}

int main()
{
int a = 0x2468;
double b;
bit_cppy(a, b); //编译的时候就会报错
}


using(重新定义)

子类可以通过使用using声明来声明继承基类的构造函数。

struct A{
A(int i) {}
A(double d, int i){}
A(float f, int i, const char* c){}
}
struct B : A {
using A::A; //继承构造函数
}


除了继承构造函数外,C++11还引进了委托构造函数。

class Info {
public:
Info() { InitRest(); }
Info(int i) : Info() { type = i; } //委托构造函数
Info(char e): Info() { name = e; }
}


noexcept

此关键字表示修饰的函数不会抛异常。用法有两种形式

void except_func() noexcept;


另一种是可以接受一个常量表达式作为参数,常量表达式会转换成一个bool类型的值,为true则函数不会抛异常,反之则有可能会抛异常。

void except_func() noexcept(常量表达式);


它更大的作用是用来保证线程安全,如析构函数声明成noexcept。

void operator delete(void *) noexcept;


export(弃用,不过未来可能留做它用)

nullptr

nullptr类型数据所占用的内存空间大小跟void *相同的。

char *cp = nullptr;//可以隐式转换成char *
//以下代码不能通过编译
//不可以转换成整型,而任何类型也不能转换成nullptr_t
int n1 = nullptr;
int n2 = reinterpret_cast<int>(nullptr);


constexpr

const保证的是运行时的常量,即具有运行时数据的不可更改性。如果需要编译时期的常量性,则就需要constExpr了。

constexpr int getConst() { return 1; }
int arr[getConst()] = {0};//这种可以通过编译,如果没有constexpr,则不能通过编译。


在函数返回类型前加入关键字constexpr来使其成为常量表达式函数。常量表达式函数比较严格,要满足下面几条要求:

- [ ] 函数体只有单一的return语句

- [ ] 函数必须返回值

- [ ] 在使用前必须已有定义

- [ ] return返回语句表达式中不能使用非常量表达式函数,全局数据,且必须是一个常量表达式。

const i = 1;
constexpr int j = 1;


上面两条语句的区别在于:如果i在全局名字空间中,编译器一定会为i产生数据,而对于j,如果不是有代码显示地使用它的地址,编译器可以不为它生数据,只做为编译时期的值。

- thread_local

现有的说明

func:预定义标识符

const char * hello() { return __func__; } //返回hello(函数名)


long long整型有两种:long long跟unsigned long long.long long以LL结尾,unsigned long long以ULL结尾

long long int lli = -9000000000LL;


宏__cplusplus

如果想确定代码是使用C++11编译器进行编译的,那么可以用下面的方法进行检测:

#if __cplusplus < 201103L
#error "should be use C++11 implementation"
#endif


这里使用了#error,不支持c++11的话,编译器就报错了。

原有的优化

支持快速初始化成员变量

在C98里,如果静态成员不满足常量性,或者常量的静态成员不是整型或枚举型,都是不能初始化的。

class init
{
private:
int c = 1; //成员无法初始化,编译报错
static int d = 0;//成员无法初始化,编译报错
static const double e = 1.3; //非整型或者枚举,编译报错
}


C++11则放开这个限制,但对于非常量的静态成员来说,C++11跟C98保持一致。

- sizeof

在C98里,对非静态成员变量使用sizeof是不能够通过编译的。但C++11里可以。

struct people{
public:
int hand;
static people *all;
}
sizeof(people::handle)是会法的


final/override控制

如果出现函数名拼写错误,参数不一致,常量性不一致,非虚函数重载这几种情况,函数后面加上了override,则编译时会报错。

外部模板

如果一个模板在某个头文件里声明了,别的要引用的话。在C98里,编译器实例化模板的时候会实例化出来两份代码,不过在C++11中,增加了外部模板的用法,只需要声明成如下:

extern template void fun<int>(int);


右尖括号>的改进

template <int i> class X{}
template <class T> class Y{}
Y<X<1> > x1; //C98里能通过
Y<X<2>> x2; //C98不能通过编译,但C++11可以


强类型枚举

声明强类型枚举非常简单,在enum后加上关键字class。

enum class Type { gen, light, medium, heavy };


智能指针

C++98中,是通过auto_ptr来实现。不过它有一些缺点(拷贝时返回一个左值,不能调用delete[]等)所以C++11里增加了unique_ptr, shared_ptr, weak_ptr。

[ ] unique_ptr:与所指对象的内存绑定紧密,不能与其它unique_ptr类型的指针对象共享所指对象的内存。如unique_ptrup2 = up1;//不能通过编译。要赋值的话,调用unique_ptr up2 = move(up1);

[ ] share_ptr:引用计数进行管理,为零时才释放

[ ] weak_ptr: 可以指向share_ptr指针指向的对象内存,却并不拥有该内存。而使用weak_ptr成员lock, 则返回其指向内存的一个share_ptr对象。且所指的对象内存无效时,返回指针空值。

相关的用法

垃圾回收机制

垃圾回收机制主要分为两大类:基于引用计数的垃圾回收器,基于跟踪处理的垃圾回收器。

lambda函数

lambda函数的语法定义如下:

[capture][parameters] mutable->return-type{statement}其中:

- [ ] capture:捕捉列表。[]是lambda的引出符,根据此来判断这个代码是不是lambda的函数。捕捉列表能够捕捉上下文的变量以供lambda函数使用。

- [ ] parameters:参数列表。如果没有参数可以跟()一块省略

- [ ] mutable: mutable修饰符。默认情况下,lambda总是一个const函数。mutable可以取消其常量性。

- [ ] return-type:返回类型。没有返回值的时候可以同符号->一块省略。

- [ ] {statement}:函数体

int main()
{
[]{};//最简单的lambda函数
int a = 3;
int b = 4;
[=] {return a + b;} //省略了参数列表与返回类型,返回类型由编译器推断为int
auto fun1 = [&](int c) { b = a + c; } //省略了返回类型,无返回值
auto fun2 = [=, &b](int c) -> int {return b += a + c; } //各个部分都完整的函数
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  读书笔记