(未整理的草稿纸)

    class A
    {
    public:
    A(int i) {};
    ~A() {}
    };

    int main()
    {
    A a(); //声明了一个返回A类型的函数a
    A b = new A;
    A
    c = new A(); //两者语义一致
    }

    A a(0);
    int main()
    {
    A b(1);
    A c = 2; //初始化阶段,等号和括号的意义相同
    }
    a构造->b构造->c构造->c析构->b析构->a析构

    void f(int i)
    {
    if (i < 10)
    goto jump1;
    X x1;
    jump1:

    }
    编译出错!跳过了x1的初始化

    void f(int i)
    {
    switch(i)
    {
    case 1:
    A a; //int a
    case 2:
    A b; //int b ————-This would be OK !
    …….
    }
    }
    switch语句中同样不允许定义类,因为可能跳过初始化但下一个case由于没break依然能用
    Aggregate initialization:
    int a[5] = { 1,2,3,4,5 };
    int b[6] = { 5 };
    int c[] = { 1,2,3,4 }; //获取数组深度:sizeof c / sizeof c;
    struct X { int i; float f; char c };
    void f()
    {
    X x[3] = { {1, 1.2, ‘a’}, {2, 2.1, ‘b’} }; //第三个X全0初始化,没有构造函数才能这么写!
    X y[3]; //都是随机数!
    }
    *

    class Display
    {
    Display(int limit) { … };
    };

    class Clock
    {
    Display hour(24);
    Display minute(60); //编译出错!只能在类的定义中调用其它类的初始构造
    }

    class Clock
    {
    Clock() : hour(24), minute(60) {};
    //Clock : minute(60), hour(24) {}; //仍然是hour先初始化,取决于成员变量顺序
    Display hour;
    Display minute;
    }



    Student::Student(string s) :name(s) //调用拷贝构造函数创建string
    Student::Student(string s) { name = s; } //调用默认构造函数创建string后再进行赋值,慢!


    符号表

    extern “C”

    #ifdef __cplusplus //被自动定义在C++文件中


    class A
    {
    void f() const { cout << “a”; } //const关键词作用于this指针,可用于构造重载
    void f() { cout << “b”; }
    private:
    int I;
    }

    A g;

    A& k()
    {
    return g; //导致输出b
    }
    const A& k()
    {
    return g; //导致输出a
    }

    int main()
    {
    k().f();
    }

    文件作用域下的const变量默认为static,除非定义为extern const int a = 123;

    inline函数一定要写在头文件中么?放在cpp中编译也能过但只有当前cpp能用,不推荐!
    函数返回引用?
    static成员变量实际上是全局变量
    static成员函数在定义时不能再加static关键词,只能在类中的声明加static
    static成员函数可以通过A::f()调用也可以通过A a; a.f();调用

    //head.h:
    class A
    {
    private:
    const static int limit;
    public:
    ….
    }
    //test.cpp
    const int A::limit = 100; //必须在文件中定义静态成员变量,若为const必须初始化


    void main()
    {
    using namespace std;
    using namespace MyLib;
    foo(); //先寻找当前块,再寻找std空间,最后寻找MyLib空间
    Cat c;
    c.Meow();
    cout << “Hello” << endl;
    }


    head1.h:
    namespace A
    {
    void f();
    }

    head2.h:
    namespace A
    {
    void g();
    }

    #include “head1.h”
    #include “head2.h”
    using namespace A;
    f();
    g(); //均可用
    在namespace中定义变量并在其它cpp中使用:
    head.h:
    namespace n1
    {
    extern int a;
    }
    namespace n2
    {
    extern int a;
    }

    vars.cpp:
    int n1::a;
    int n2::a;

    main.cpp:
    using namespace n1;
    a = 1;






    class A
    {
    int I;
    void ptr() { cout << “A” };
    A(const int n) : i(n){};
    }

    class B : public A
    {
    int k;
    void ptr() { cout << “B” };
    B() : A(47), k(31){}; //通过初始化列表给父类的初始化传递参数
    }

    B b;
    b.ptr() //输出B;
    B::f()可以通过A::ptr()调用父类A的ptr函数,B类的外部只能调用B的ptr函数



    class A
    {
    public:
    int I;
    }

    class B : public A
    {
    int k;
    B() : k(31), i(47); //Error!任何情况下父类的成员必须由父类的构造函数初始化
    }

    父类的构造函数先于子类的构造函数执行,析构函数相反


    class A
    {
    public:
    void prt(){ cout << “A1” }
    void prt(const int n){ cout << “A2” }
    void prt(const double d){ cout << “A3” }
    }

    class B : public A
    { … }

    B b;
    b.prt() //输出A1
    b.prt(123) //输出A2
    b.prt(1.23) //输出A3

    改变B类的定义为
    class B : public A
    {
    prt(){ cout << “B” }
    }
    b.prt() //输出B
    b.prt(123) //Error!需要调用b.A::prt(123)
    b.prt(1.23) //Error!需要调用b.A::prt(1.23)

    class B{ using A::prt; … } //可以直接调用所有名为prt的父类函数


    把子类的对象当做父类的对象看待:up-casting
    class A
    {
    virtual void prt(){ cout << “A” }
    };

    class B
    {
    void prt(){ cout << “B” }
    };

    A a;
    B b;
    Ap = &b; //up-casting
    a.prt(); //输出A
    b.prt(); //输出B
    p->prt(); //输出B!根据指向变量的实际类型调用prt,动态类型!

    如果class B没有prt函数,则调用父类A的prt函数输出A


    class A class B : public A
    { {
    virtual void f(); virtual void f();
    int I; }
    }
    32位下A类占用8个字节,前4字节是Virtual-Pointer,指向Virtual-Table中的项,Virtual-Table中存放虚函数指针。每个对象拥有一个Virtual-Pointer,每个类拥有一个Virtual-Table。

    Virtual-Pointer在构造函数中确定,对象之间互相赋值不会赋值Virtual-Pointer,对象创建以后没有任何方式改变其Virtual-Pointer,Virtual-Pointer代表了对象的动态类型


    B b;
    A a = b;
    a->f() //调用A类的f函数

    B
    b = new B;
    A a = b;
    a->f() //调用B类的f函数


    当类可能作为基类被over-write时,析构函数应该为virtual。否则派生类析构时只会调用基类的析构函数。A
    a = new B; delete a; 仅仅调用了A类的析构函数

    友元可以提高程序的运行效率但是会破坏封装性。
    友元关系不可以被继承。



    int main()
    {
    int a[5][5];
    int(p)[4]; //数组指针,指针数组是int p[4]
    p = (int()[4])a;
    printf(“%d\n”, &p[4][2] - &a[4][2]);
    }
    输出结果为-4,p为数组指针。p是一个指向包含4个int元素的一维数组的指针,p[i]相对于p[i-1]增长了4个元素,因此p[4][2]为第18个元素而a[4][2]是第22个元素。两者的地址虽然相差16,但是计算的时候是两个指针类型的变量在计算,因此输出的结果为-4


    空的类占据1个字节的空间,假设class A为空类,为了防止A a和int i的地址一样,强行为A类分配1个字节的空间保证地址的独立



    Person::Person(const Person& p)
    {
    name = p.name; //private的保护针对类而言而非对象,p私有成员可以访问
    }

    Person baby_a(“Fred”);
    Person baby_b = baby_a;
    Person baby_c(baby_b); //三者均为初始化而非赋值




    const A operator+(const A& b);
    A x, y;
    y = x + 3 //必须要有需要一个int类型参数的构造函数,隐式构造A的对象

    x+3相当于调用x.operator+(A(3));
    3+x要么将重载函数写为全局函数,传递两个参数,将3利用构造函数构造为A(3);要么编写operator int强制类型转换重载将x转换为int。

    运算符的重载可以写在类中作为类的成员函数:
    A operator+(const A&b);
    也可以写在全局中,并在类内声明为友元:
    Friend A operatr+(const A&a, const A&b); //需要两个参数

    =(赋值)、()、[](取下标)、->、->
    都必须作为成员函数重载
    其它二元运算符重载建议作为全局函数编写
    + - / % ^ & | ~
    const T operatorX(const T& l, const T& r);

    ! && || < <= == >= >
    bool operator(const T& l, const T& r);

    []
    E& operator;

    ++ — (考虑前缀和后缀)
    const A& operator++()
    {
    i++; return
    this;
    }
    const A operator++(int)
    {
    A a(this); i++; return a
    }

    T& operator=(const T&rhs)
    {
    delete m;
    m = new E[100];
    for (…)
    m[i] = rhs.m[i];
    return
    this;
    }
    注意c=c这样的赋值!delete m后就无法拷贝,需要在一开始判断赋值端是否是本身。
    不带有指针的类可以用默认的赋值,带有指针的类的赋值就可能涉及内存delete

    class A
    {
    operator B(); //可以编写基本类型强制类型转换,但不建议编写operator T()
    }
    class B
    {
    B(A){};
    }
    A a;
    B b = a; //二义性!




    模板函数实际上时声明而不是定义,编译器在编译时会自动生成相应的代码。
    template //或者template
    swap(T&a, T&b)
    {
    T temp = a;
    a = b;
    b = temp;
    }

    string s1, s2;
    //swap(string&, string&) ; 编译器会自动插入此函数声明。
    swap(s1,s2);

    倘若我们同时有swap(T&, T&)与swap(string&, string&),swap(s1,s2)会调用用户自定义的函数


    类模板的所有成员函数都是函数模板
    template
    class Vector
    {

    }

    template
    Vector::Vector(int size)
    {
    m_element = new T[size];
    }

    Template
    Vector::~Vector(){}


    Vector.cpp
    template
    Vector::Vector(int size){}



    Main.cpp
    Vector v;
    编译出错!Vector.cpp中虽然写出了所有成员函数,但都只是声明,找不到定义。必须把所有的东西都放到头文件!与inline函数类似!

    模板类的静态成员变量,找不到合适的地方存放,因此模板类不能使用静态成员变量。
    template
    Int Vector::key; //错!1.头文件中不能放定义;2.template只能作用于函数或类
    除非只用在单个CPP中,所有都定义在CPP,template Type::key的定义是可以的

    Main.cpp
    void f(Vector&);
    int main()
    {
    Vector v1;
    f(v1);
    }

    a.cpp
    void f(Vector&)
    {

    }

    Main.cpp与a.cpp两个cpp是分开编译的,编译的时候编译器分别为它们生成了一份Vector的样板,包括所有的成员函数,理论上来说会有同名函数的重定义,但是程序却可以运行没有报link error。模板类的所有函数都被默认加上了关键词weak,两个带有weak的同名函数会被简单合并;一个带有weak一个不带有weak时选择不带weak的函数。


    catch() //能够抓住任何类型的异常,但是无法获得异常的对象

    catch的三种情况:
    1. throw在try内,且有适当的catch匹配,则直接执行并往下走。
    2. throw在try内,且没有适当的catch匹配,则一路退出直到找到适当的catch。
    3. throw不在try内,则一路退出直到找到适当的catch。

    class A;
    class B : public A;
    ….
    catch(A){…}
    catch(B){…} //永远不会被执行

    建议的使用方法:不要抛出new的对象而抛出本地变量,接收变量的引用。

    void f() throw(int, double) //只能抛出int和double类型否则出错
    void f() throw() //不能抛出任何异常
    void f() //可以抛出任何异常

    构造函数和析构函数中不要抛出异常,也不要申请资源(例如new,fopen),否则A *p = new A(); 中抛出异常时,new出来的空间会成为野内存空间,p还没有被赋值;delete时调用析构函数但是析构函数抛出异常跳过了delete的内存回收。


    区别以下两个区别,如果ABCD四个操作时一个连贯的操作,则应该将他们写在同一个try语句中,保证任何错误都会中止接下来的操作。右边的逻辑是不对的!
    try try{A}
    { catch(A){…}
    A; B; C; D; try{B}
    } catch(B){…}
    catch(A){…} try{C}
    catch(B){…} catch(C){…}
    catch(C){…} try{D}
    catch(D){…} catch(D){…}