func预定义标识符

func预定义表示符的基本功能是返回所在函数的名字

  1. #include<string>
  2. #include<iostream>
  3. using namespace std;
  4. const char* hello(){return __func__;}
  5. int main(){
  6. cout<<hello()<<endl;
  7. }

编译器会默认隐式地在函数的定义后定义func标识符;实际代码相当于如下:

  1. const char* hello(){
  2. static const char* __func__="hello";
  3. return __func__;
  4. }

但是不允许作为函数参数的默认值,因为在声明时并不会被定义

_Pragrma

#pragrma是预处理指令,可以向编译器传达语言标准之外的信息。
例如#pragrma once指示编译器该头文件应该只能编译一次。
在C++11中,定义了和#pragrma相同功能的操作符_Pragrma
使用方式为_Pragrma(字符串常量);例如_Pragrma("once");
好处是可以用在宏中展开!

  1. #define CONCAT(x) PRAMGA(concat on #x)
  2. #define PRAGMA(x) _Pragma(#x)
  3. CONCAT(..\concat.dir)

变长参数宏定义以及VA_ARGS

C99标准中可以使用变长参数宏定义,即宏定义中参数列表最后一个参数为省略号。

  1. #define PR(...) printf(__VA_VARGS__)

__VA_VARGS__可以在宏定义的实现部分替换省略号代表的字符串

宏__cplusplus

why need it

在C和C++混合编写的代码中,常常看到如下的代码

  1. #ifdef __cplusplus
  2. extern "C"{
  3. #endif
  4. //....
  5. #ifdef __cplusplus
  6. }
  7. #endif
  1. 因为C++编译器会对函数名称进行重整,所以对于C的函数不是很好支持
  2. extern “C”可以抑制对于函数名变量等符号的重整

    检测版本

  3. __cplusplus宏可以表示该文件用C++来编写,实际上在C++11中,此宏并非只有被定义了和未被定义两种状态,还表示一个整型值,整型值表示当前C++的版本

    1. #if __cplusplus < 201103L
    2. #error "should use C++11 implementation
    3. #endif

    静态断言

    断言是什么?

    断言是将一个返回值总是真的判别式放在语句中,用于排除在设计逻辑上不应该发生的情况
    通常而言,断言只是用于调试程序。但是断言仅仅实在程序运行时才能起效,我们希望在编译时做出一些断言。

    静态断言

    因此C++11引入了静态断言static_assert,即可以在编译时期进行检测的断言 :::warning static_assert(a,"b");

  • 参数a:断言表达式,返回一个bool值
  • 参数b:一个警告信息,即一个字符串 ::: static_assert是在编译时进行的,因此使用范围比assert更广一点。

    noexcept修饰符和操作符

    有时候我不希望抛出异常,比如copy操作中,析构函数中,一旦抛出异常,可能会导致内存泄漏等安全问题的发生。
    C++11中表示函数不会抛出异常的声明由noexcept修饰,如果noexcept修饰的函数抛出了异常,编译器可以选择直接调用std::terminate()函数来终止程序的运行,比throw要高效一些

    两种使用形式

    void excpt_func() noexcept;
    void excpt_func() noexcept (常量表达式);
    常量表达式会被转换成bool类型,如果值为true,则表示不会抛出异常,反之则可能抛出,不带参数的修饰表示不会抛出异常。

    不抛出异常的情况

    对于delete来讲,默认是设置成noexcept的
    对于析构函数而言,默认是设置成noexcept的,除非显示声明了。

非静态成员的sizeof

sizeof是一个C中遗留下来的运算符,和加减乘除一样。C++98中对于非静态成员变量使用sizeof不能通过编译,但是C++11支持了这一点。

  1. struct People{
  2. public:
  3. int hand;
  4. static People* all;
  5. }
  6. int main(){
  7. People p;
  8. cout<<sizeof(p.hand)<<endl;
  9. cout<<sizeof(People::all)<<endl;
  10. cout<<sizeof(People::hand)<<endl;//only C++11
  11. }

C++98中只有静态成员,或者对象的实例才能对其成员进行sizeof操作

final/override控制

重载 overload

如果A中的虚函数func被其派生类B再次定义了,那么称B重载了A的函数func

final

有时候我们不希望某个函数被重载,因此在派生类中可以将他声明为final

  1. struct Object{
  2. virtual void fun()=0;
  3. }
  4. struct Base:public Object{
  5. void func() final;//不允许再重载了
  6. }
  7. struct Derived:public Base{
  8. void func(); //无法通过编译,因为final修饰的函数不允许被重载
  9. }

final通常只在继承关系的中途终止派生类的重载中有意义。

override

如果某个虚函数必须被重载,但是出于种种原因(如拼写错误,函数参数错误等),可能实际在派生类中并未重载,这会导致错误。
C++11提供override描述符,表示该函数必须重载基类中同名的函数,否则无法通过编译

  1. struct Base{
  2. virtual void Turing()=0;
  3. virtual void VNeumann(int g)=0
  4. }
  5. struct Derived :public Base{
  6. void Turning() override; //ok!
  7. void VNeumann(double g) override;//非重载,无法通过编译
  8. }

模板函数的默认参数

在C++98中可以允许模板类由默认的模板参数,其功能类似于函数的默认形参,但是模板函数在C++98中并不允许这一点。

  1. template<typename T=int>
  2. class Defclass{};
  3. template<typename T=int>
  4. void DefTempParm(){};//C++11通过,C++98失败
  • 对于类模板来说,对多个参数指定默认值时需要从右往左
  • 对于函数模板而言不需要这一点。

    模板函数的默认形参不是模板参数推导的依据,函数模板参数的选择总是由函数的实参推导而来的