1.变长参数宏定义及__VA_ARGS__C99中,可使用变长参数空定义,即在宏定义参数列表最后一个参数为省略号。
预定义宏__VA_ARGS__可在宏定义实现部分替换省略号代表的内容。
#include <stdio.h> #define LOG(...) {\ fprintf(stderr, "%s:Line %d:\t", __FILE__, __LINE__);\ fprintf(stderr, __VA_ARGS__);\ fprintf(stderr, "\n");\ } int main() { int x = 3; LOG("x=%d", x); return 0; }2.final与overridefinal可使得类型指定相应的虚函数对派生类来说不可被继续重载。
#include <iostream> class B { public: virtual void fun() final { printf("Base_fun()\n"); } }; class D1 : public B { public: void fun() // err { printf("D_fun()\n"); } }; class D2 :public B { }; class DD : public D2 { public: void fun() // err { printf("DD_fun()\n"); } }; int main() { }被final修饰的虚函数,无法在直接派生类和间接派生类被重载。
override可使得类型定义的函数必须是基类某个虚函数的重载时才可通过编译。
#include <iostream> class B { public: virtual void fun() { } }; class D : public B { public: void fun() override { } void fun(int) override // err { } }; int main() { return 0; }类型中被override修饰的函数,编译器将检查,确保此函数重载了直接基类或间接基类的某个虚函数时,方可编译通过。
3.委托构造
#include <iostream> using namespace std; class Base { public: Base(char c){ cout << "Base_" << c << endl; } }; class DCExcept{ public: DCExcept(double d) try:DCExcept(1, d){ cout << "Run the body." << endl; } catch(...){ cout << "caught exception." << endl; } private: DCExcept(int i, double d):b('b') { cout << "going to throw!" << endl; } int type; double data; Base b; }; int main() { DCExcept a(1.2); return 0; }新标志支持委托构造上述DCExcept(double d)是委托构造函数,DCExcept(int i, double d)是委托构造的目标构造函数。
关于委托构造注意点:
(1). 委托构造函数初始化列表除了指定目标构造函数的委托信息,不可包含其他任何信息。这样保证类的初始化只通过目标构造函数执行一次。
(2). 触发委托构造函数时的执行顺序为:
a.通过目标构造函数完成构造阶段,执行目标构造的函数体。
b.执行委托构造的函数体。
c.采用委托构造时,支持上述用try修饰目标构造函数。这样可像上述在委托构造中添加catch语句来捕获目标构造中可能抛出的异常。
4.union类型c++11中union类型中允许出现自定义类型成员,标准库类型成员。
#include <iostream> class B { public: B(){ printf("B()\n"); } B(int i){ printf("B(int)\n"); } B(const B&){ printf("B(const&)\n"); } int i; }; class A { public: A(int i) { printf("A(int)\n"); } ~A() { } // union类型特点在于只分配内存,不执行构造过程。 // 其他c++类型通过new动态得到对象时,执行内存分配,对象构造两个过程。 union { B n1; B b; }; }; int main() { A a(1); return 0; }
值得注意的是union对象在动态构造阶段,只是分配了内存,不触发union对象的构造。
5.usingusing在c++中主要用于两种目的:
(1).提升命名空间可见性
(2).别名功能
这里讲述其别名功能。
typedef std::vector<std::string> strvec; using stdvec2 = std::vector<std::string>;上述利用typedef和using均可实现类型别名功能。
但using还可用于模板编程时的别名,typedef则无法达到此效果。
template<typename T> using MapString = std::map<T, char*>; MapString<int> numberedString;6.模板重载和类型匹配
6.1.函数模板支持重载
#include <iostream> struct Test{ typedef int foo; }; template <typename T> void f(typename T::foo){ printf("T::foo\n"); } template <typename T> void f(T){ printf("T\n"); } int main(){ // 1.推断模板参数(隐式或显式),这里是显式 // 2.结合模板定义,得到实例定义(非模板实现实例)。不合法的定义不会生成实例。 // 3.接触实参进行函数匹配 f<Test>(10); f<int>(10); return 0; }c++支持模板函数的重载。在存在多个模板重载版本时,总是试图先用受限版本去匹配,再用非受限版本去匹配。
6.2.类模板支持特化
#include <iostream> template <typename T> class A { public: A(T t) { printf("A1_value_%d\n", t); } }; template <typename T> class A<T*> { public: A(T* t) { printf("A2_addr_%x_value_%d\n", t, *t); } }; int main(){ int t = 11; // 1.模板类型推断(显式或隐式)-这里是显式 // 2.基于模板定义结合模板类型推断得到实例定义。不合法的定义不会生成实例。 // A<T*>对于A<int>来说是非法的,因为int不是指针类型,所以不会生成A<int>实例。 // 3.结合实参进行函数匹配 A<int> a(10); // 1.模板类型推断(显式或隐式)-这里是显式 // 2.基于模板定义结合模板类型推断得到实例定义。不合法的定义不会生成实例。 // 这里有两个合法的实例定义 // 3.结合实参进行函数匹配 // 实际匹配到<T*>版本的实例定义 // 4.针对匹配版本进行使用 A<int*> aa(&t); return 0; }
类模板的特化与函数模板的重载还是不同的。
特化时,只能通过对通用版本的模板参数添加修饰符或用具体类型代替来达到特化效果。像函数重载中形如T::foo这样的操作是不允许的。