1.变长参数宏定义及__VA_ARGS__
C99中,可使用变长参数空定义,即在宏定义参数列表最后一个参数为省略号。
预定义宏__VA_ARGS__
可在宏定义实现部分替换省略号代表的内容。
#include
#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与override
final可使得类型指定相应的虚函数对派生类来说不可被继续重载。
#include
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
class B
{
public:
virtual void fun()
{
}
};
class D : public B
{
public:
void fun() override
{
}
void fun(int) override // err
{
}
};
int main()
{
return 0;
}
类型中被override修饰的函数,编译器将检查,确保此函数重载了直接基类或间接基类的某个虚函数时,方可编译通过。
1.委托构造
#include
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
语句来捕获目标构造中可能抛出的异常。
2.union类型
c++11中union类型中允许出现自定义类型成员,标准库类型成员。
#include
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
{
int n;
B b;
};
};
int main()
{
A a(1);
return 0;
}
值得注意的是union对象在动态构造阶段,只是分配了内存,不触发union对象的构造。
3.using
using在c++中主要用于两种目的:
(1).提升命名空间可见性
(2).别名功能
这里讲述其别名功能。
typedef std::vector strvec;
using stdvec2 = std::vector;
上述利用typedef和using均可实现类型别名功能。
但using还可用于模板编程时的别名,typedef则无法达到此效果。
template
using MapString = std::map;
MapString numberedString;
4.模板重载和类型匹配
4.1.函数模板支持重载
#include
struct Test{
typedef int foo;
};
template
void f(typename T::foo){
printf("T::foo\n");
}
template
void f(T){
printf("T\n");
}
int main(){
f(10);
f(10);
return 0;
}
c++支持模板函数的重载。在存在多个模板重载版本时,总是试图先用受限版本去匹配,再用非受限版本去匹配。
4.2.类模板支持特化
#include
template
class A
{
public:
A(T t)
{
printf("A1_value_%d\n", t);
}
};
template
class A
{
public:
A(T* t)
{
printf("A2_addr_%x_value_%d\n", t, *t);
}
};
int main(){
int t = 11;
A a(10);
A aa(&t);
return 0;
}
类模板的特化与函数模板的重载还是不同的。
特化时,只能通过对通用版本的模板参数添加修饰符或用具体类型代替来达到特化效果。像函数重载中形如T::foo
这样的操作是不允许的。