C++点点滴滴(一)

//本系列用来记录学习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};
View Code

还不是太明白, 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 }
View Code

||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

 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;
View Code

只有静态常量成员可以在类内初始化,如 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 }
View Code

会输出无穷行……原因是:如果类的析构函数定义中有调用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.

原文地址:https://www.cnblogs.com/JustForCS/p/4844940.html