深入理解C++11 读书笔记(二) 稳定性和兼容性

8 篇文章 0 订阅
本文详细介绍了C++11为了兼容C99引入的新特性,包括预定义标志`__func__`,_Pragma操作符,变长参数宏定义等。此外,还讨论了long long整形,整型扩展,静态断言,noexcept修饰符,快速初始化成员变量等关键更新,旨在提升代码稳定性和兼容性。
摘要由CSDN通过智能技术生成

兼容C99

C99标准在C++98之后,因此C++11为了兼容C99,增加预定义宏
兼容的宏

预定义标志 __func__
  • 函数体内直接使用 __func__ 表示函数名的字符串,__func__等效于const static char * __func__ = "***"
  • 不能作为函数参数的默认值,此时还未定义。不过可以在构造函数的初始化列表里使用。
_Pragma操作符
  • _Pragma是操作符,#pragma是预编译指令,_Progma(“once”)和#pragma once等价,但是操作符能够在宏中展开,#pragma不能。因此更有灵活性
变长参数宏定义
  • 变长参数的宏定义最后一个是省略号,使用时__VA_ARGS__ 实现部分代表省略号的字符串。
#define PR(...) printf(__VA_ARGS__);
#define LOG(...) fprintf(stderr,__VA_ARGS__);

longlong 整形

  • long long 没被C++98标准支持,但在C99标准里。
  • C++11 标准要求在不同的平台,long long位数可以不同,但至少64位。
  • 写常数字面量时后缀 LL(ll) 或ULL(ull) 表示 long long, unsigned long long
  • <climits>里宏LLONG_MIN、LLONG_MAX,ULLONG_MIN代表平台相关值。
  • printf时使用符号 %lld %llu

整型扩展

  • 程序里常见到的__int64,UINT,int64_t等 源自编译器自行扩展或来自某些编程环境(Linux内核代码)。
  • standard integer type:C++11标准只定义了5种有符号整型:signed char; short int; int; long int; long long int; 每种有符号整型对应一种无符号整型 unsigned。
  • extended integer type:可扩展为比最长的整型还长,或在标准整型位数之间。比如128位或48位。
  • 整型提升时,等级低的扩展为等级高的。长度长的等级高,相同时 标准整型等级高于扩展整型。

宏__cplusplus

C和C++混编时经常有

#ifdef __cplusplus
extern "C"{
#endif
//code
...
#ifdef __cplusplus
}
#endif
  • extern "C" 可以抑制C++对函数名,变量名等符号的重整(name mangling),因此编译出的C或C++目标文件中发符号都是相同。
  • __cpluscplus被定义为整型,C++03标准中是 199711L,C++11标准中是201103L。比如想确定代码是C++11支持的编译器编译,可用
#if __cpluscplus < 201103L
#error "should use c++11 implementation"
#endif

静态断言

  • <cassert> 里的assert是运行时断言,可以用NDEBUG宏来禁用
  • 配合#if#error可以实现编译器 预处理 期间的断言,很常见的用法
    GNU 的cmathcalls.h代码块
#ifndef _COMPLEX_H_
#error "Never use<bits/cmathcalls.h> directly;include<complex.h> instead."
#endif
static_assert (bool, char * txt)
  • 静态断言,在编译期间进行断言,并不需写在函数内部。
  • 由于是编译期间,因此参数必须是常量表达式
  • 应用场景:比如检查 按位定义的enum的完备性。static_assert(eMax - 1 == eEnum1 | eEnum2 |eEnum3…)。或在模板里检查 static_assert(sizeof(b) == sizeof(a))
  • Boost内置的BOOST_STATIC_ASSERT利用除0 编译器会报错实现静态断言,类似以下实现
#define boost_static_assert(e)
do{
enum{
    assert_static__ = 1 / (e);
}
}while(0)

noexcept 修饰符和 noexcept操作符

  • noexcept放在函数之后,可带一个bool类型的constexpr常量表达式,缺省true。true表示该函数不会抛出异常,false表示可能会抛出异常。
  • 和之前的throw-catch不同的是,catch之后会导致函数栈依次展开(unwind),对已经构造的对象调用析构函数,而noexcept(true)如果内部抛出异常,会通过系统的std::terminate直接结束程序
  • 析构函数默认 noexcept(true).
  • noexcept作为操作符,可用于模板
template<class T>
void fun() noexcept(noexcept(T())) {}

其中第二个noexcept接收的参数是一个是否会抛出异常的表达式。利于泛型编程。


快速初始化成员变量

  • c++98标准中对类成员的就地初始化非常限制,只能对const static 的整型或枚举使用 等号 进行就地初始化。(g++可以对const static double就地初始化,这是GNU的扩展,非标准)
  • c++11 可以对非静态变量使用”=”或”{}”进行就地初始化。
struct D{
    int a = 1;
    double b = 2.0;
    double c{3.2}
}
  • 当就地初始化和初始化列表同事存在时,初始化列表优先于就地初始化,先进行就地初始,再初始化列表。(此处有疑问:如果是自定义对象,如果在就地初始化调用了构造函数,那在初始化列表时怎样表现呢?还会调用构造函数吗?还是如果有覆盖,就不调用就地初始化了?待考证)
  • 对于非const 的静态成员变量,还是需要在类外定义。

非静态成员的 sizeof

  • C++98中 类的非静态成员要得到大小,需要有类(class P)的实例,sizeof(p->data).如果用trick的方法sizeof( ((P*)0)->data)
  • C++11中 可以直接使用sizeof(P::data),不需要实例。
  • 静态成员在98和11都可以直接使用sizeof(P::static_data)

扩展的friend语法

  • C++11比C++98在friend语法上的改进有 声明友元类时不需要’class’关键字,可以用typedef改名后的类名。
  • 可以为模板声明友元类,friend class T如果T不是类,则此句没意义,并不会有错误。利用这个规则,可以为类模板定义普通使用的类,和可以访问私有数据的friend class的类。
template <class T>
class Base{
friend class T;
}

using unaccessBase = Base<int>
using accessBase = Base<friendClass>

final / override关键字

  • final 阻止一个虚函数被子类继续继承下去
  • override 显示表示该函数重载了父类的虚函数,如果因为书写错误并没有继承,则会编译报错,方便程序员书写。

模板函数

  • C++11 支持默认模板参数,与类模板 必须遵循‘从右往左’指定不同,函数的默认模板参数不限制,从函数的实参进行推导。
  • 外部模板是对模板性能的改进。编译器会在 每一个使用模板函数的cpp编译时实例化一份代码(目标文件中都有实例化类的代码),链接器在链接时将不同目标文件里相同的模板实现只保留一份。如果很多文件使用同样的模板,会非常影响编译速度。(此处书里只针对模板函数,不知模板类是否也有同样的问题)
    模板编译过程

因此改进的方案是 显示实例化模板,声明外部模板实例

//delcare t.h
template<class T>
void funT();

extern funT<int>();

//defination a.cpp
void funT<int>()


//use b.cpp
{
funT<int>();
}
  • 局部和匿名类型都可以作为模板实参
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值