//本系列用来记录学习C++中的点点滴滴
1、自动聚合初始化

1 #define DF(N) void N() { 2 cout << "function " << N << "called ..." << endl;} 3 DF(a); DF(b); DF(c); DF(d); DF(e); DF(f); DF(g); 4 void (*fun[])() = {a, b, c, d, e, f, g};
还不是太明白, Mark之~
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
2.define中的#与##作用
如上的代码中define中有#,那么#与##的作用是什么呢,两者都是用来拼接字符串的,但是又有细微的差别。
#是字符串化的意思,出现在宏定义中的#是把跟在后面的参数转成一个字符串
Example:
#define strcpy__(dst, src) strcpy(dst, #src) strcpy__(buff,abc) 相当于 strcpy__(buff,“abc”)
##是连接符号,把参数连接在一起
#define FUN(arg) my##arg 则 FUN(ABC) 等价于 myABC
详细的例子:
#include <iostream> using namespace std; #define OUTPUT(A) cout<<#A<<":"<<(A)<<endl; int main() { int a = 1, b = 2; OUTPUT(a); OUTPUT(b); OUTPUT(a + b); return 1; }
应该挺明白了
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3.C&&C++中的struct/class有不同,以前以为他们的不同只是在public/private方面,既struct默认成员为public,而class默认成员为private,然后还有别的区别……
在C中struct内不能有函数,而在C++中struct内却能有函数!!!
如果想在C的struct内用函数,那就只能用指针了。。。
4.看下面的代码有什么问题
class C; class A { public: class B { public: C c; }; }; class C { public: int val; C& operator =(const C& c) { val = c.val; return *this; } }; int main() { A a; C c; A::B b; b.c = c; return 0; }
当然会报错: error: field `c' has incomplete type
这是因为类或结构体的前向声明只能用来定义指针对象,因为编译到这里时还没有发现定义,不知道该类或者结构的内部成员,没有办法具体的构造一个对象,所以会报错。
将类成员改成指针就好了。
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
5.assert的使用总结
assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:
#include <assert.h> void assert( int expression );
assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。
下面例子中由于a不存在,所以fopen失败。由于assert的存在,程序将不会执行puts("hello")这一行。
#include <iostream> #include <string> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <vector> #include <set> #include <map> #include <cstdlib> #include <cassert> using namespace std; #define INF 1000000007 #define MIN(a, b) (a > b ? b : a) #define MAX(a, b) (a > b ? a : b) #define MAXN 1005 #define maxn 10005 int main() { FILE *fp; fp = fopen("a", "r"); assert(fp); puts("Hello"); return 0; }
在调试结束之后可以通过添加#define NDEBUG来禁止assert()的使用,即如下所示:
#define NDEBUG #include <cassert>
注意事项:
1、不能使用改变环境的语句,因为assert只在DEBUG个生效,如果这么做,会使用程序在真正运行时遇到问题。
例如:
assert(i++ < 100);
2.每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败。
例如:
assert(1 < 0 && 2 < 1);
3.有的地方,assert不能代替条件过滤。
assert是用来避免显而易见的错误的,而不是处理异常的。错误和异常是不一样的,错误是不应该出现的,异常是不可避免的。c语言异常可以通过条件判断来处理,其它
语言有各自的异常处理机制。
一个非常简单的使用assert的规律就是,在方法或者函数的最开始使用,如果在方法的中间使用则需要慎重考虑是否是应该的。方法的最开始还没开始一个功能过程,在一个
功能过程执行中出现的问题几乎都是异常。
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
6.将一个类的成员函数作为另一个类的友元函数,而且该成员函数访问另一个类的private成员
class C1; class C2; class C3 { public: void showSelf(C1); }; class C1 { private: int num; friend class C2; friend void C3::showSelf(C1); }; void C3::showSelf(C1 c1) { cout << c1.num << endl; } class C2 { public: int count; };
破坏封装了,好丑好丑好丑啊!!!
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
7.g++现在支持 数组长度可为非常量
int n; cin >> n; int arr[n];
这种方法在standard C++中是错误的,在C99中是正确的。
但是g++貌似做了扩展,用g++编译时,这个语句是正确的...g++中能编译不代表它就是正确的。
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
8.如果一个函数返回一个临时变量,const引用绑定这个临时变量,那么它会延长这个临时变量的寿命与自己一样???
这是一种错误的说法,返回的是临时变量而不是临时变量的地址,所以会复制生成一个临时对象,引用绑定的实际是那个临时对象,和函数内的临时变量没有关系了。右值不能被绑定到左值引用,但可以绑定到常量左值引用和右值引用,所以把函数返回值作为参数的时候,形参应为const。比如:
class X{}; X f() { return X(); } void g1(X&) {} void g2(const X&) {} g1(f());//Error g2(f());//OK
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
9.const 修饰“最靠近”它的那个,既const int* u;为u是一个指针,它指向一个const int
const int *u;//u所指向的地址的值不能改变 int const *v;//同上 int d =1; int* const w = &d;//指针不能改变,而指针指向的地址的值可以 int const* const x2 = &d;//指针和值都不能改变
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
10.类的成员函数后面加 const,表明这个函数不会对这个类对象的数据成员(准确地说是非静态数据成员)作任何改变。
class T { public: int val; void fun() const { val += 1; //Error 禁止改变数据成员的值 } };
mutable关键字
关键字mutable是C++中一个不常用的关键字,他只能用于类的非静态和非常量数据成员。
如果一个类的成员函数被声明为const类型,表示该函数不会改变对象的状态,也就是该函数不会修改类的非静态数据成员。但是有些时候需要在该类函数中对类的数据成员进行赋值,这个时候就需要用到mutable关键字了。
class T { public: mutable int val; //mutable void fun() const { val += 1; //OK } };
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
11.静态对象的销毁
静态对象的销毁按与初始化时相反的顺序进行,全局对象在main()执行之前被创建,在推出main()时销毁。如果一个包含局部静态对象的函数从未调用过,那么这个对象的构造函数也不会执行,也不会执行析构函数。具体看下面代码以及其执行结果。

1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <algorithm> 5 #include <string> 6 #include <cstring> 7 #include <vector> 8 #include <list> 9 #include <queue> 10 11 using namespace std; 12 13 class Obj 14 { 15 char c; 16 public: 17 Obj(char cc) : c(cc) 18 { 19 cout << "Obj::Obj() for " << c << endl; 20 } 21 ~Obj() 22 { 23 cout << "Obj::~Obj() for " << c << endl; 24 } 25 }; 26 27 Obj a('a'); 28 29 void f() 30 { 31 static Obj b('b'); 32 } 33 34 void g() 35 { 36 static Obj c('c'); 37 } 38 39 int main() 40 { 41 cout << "inside main()" << endl; 42 f(); 43 cout << "leaving main()" << endl; 44 45 return 0; 46 }
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
12.static成员初始化
在C++中,类的静态成员必须在类内声明,在类外初始化。如下中cnt的初始化。

1 class Monitor 2 { 3 static int cnt; 4 public: 5 void print() 6 { 7 printf("%d ", cnt); 8 } 9 void incident() 10 { 11 printf("incident() is called ! "); 12 cnt++; 13 } 14 }; 15 int Monitor::cnt = 0;
只有静态常量成员可以在类内初始化,如 static const int cnt = 0;
常量成员或者是静态成员都不能直接在类内进行初始化。
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
13.exit()
下面程序执行会发生什么样的事情?

1 #include <iostream> 2 #include <string> 3 #include <cstdlib> 4 #include <cstdio> 5 #include <cstring> 6 #include <algorithm> 7 #include <vector> 8 #include <queue> 9 10 using namespace std; 11 12 class Test 13 { 14 public: 15 ~Test() 16 { 17 printf("~Test() is called ! "); 18 exit(0); 19 } 20 }; 21 22 Test t; 23 24 int main() 25 { 26 return 0; 27 }
会输出无穷行……原因是:如果类的析构函数定义中有调用exit(0),并且创建了一个该类的静态全局对象,那么当析构函数被显示或者隐式地调用时,exit(0)会反过来调用静态存储区类的析构函数。这样就造成了无穷递归。不过这种行为实际上是未定义的行为,所以可能发生很多神奇的事情
From the C++ standard (§3.6.1/4):
Calling the function
void exit(int);
declared in <cstdlib> (18.3) terminates the program without leaving the current block and hence without destroying any objects with automatic storage duration (12.4). If exit is called to end a program during the destruction of an object with static storage duration, the program has undefined behavior.