zoukankan      html  css  js  c++  java
  • 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.

  • 相关阅读:
    如何有效阅读一本书:超实用笔记读书法
    typescript 为什么使用 let呢
    typescript step by step
    typescript 第一弹
    oracle 闪回操作(flashback)
    oracle自动备份
    将Tomcat添加至开机自启动
    svg 鼠标形状 小手
    jquery 多个checkbox的联动选择
    java中奇偶数的判断
  • 原文地址:https://www.cnblogs.com/JustForCS/p/4844940.html
Copyright © 2011-2022 走看看