zoukankan      html  css  js  c++  java
  • C++盲点

    const

    const指针

    1 char greeting[] ="hello"; 
    2 char* p = greeting; // non-const pointer, non-const data
    3 const char* p = greeting; // non-const pointer, const data
    4 char* const p = greeting; // const pointer, non-const data
    5 const char* const p = greeting; //const pointer, const data
    1 void f1(const Object* o); 
    2 void f2(Object const* o); 

    这两个是一样的;

    const vector<int>::iterator更像是T* const; 

    vector<int>::const_iterator更像是const T*;

    enum不能取地址,const可以取地址,define没有任何约束;

    const函数是为了可以操作const对象;

     1 class Test {
     2 public:
     3     void print() const {}        
     4     void mut() const { m1 = 1; }
     5 private:
     6     mutable int m1; // can be changed even in const functions
     7 }; 
     8 const Test t;
     9 t.print(); 
    10 t. mut(); //legal

    const版本和非const版本的代码重用。实现const函数,在non-const函数中调用const版本。

    1 class Test {
    2 public:
    3    const int& operator[](int i) const {...} 
    4     int& operator[](int i) {
    5         const_cast<int&>(static_cast<const Test&>(*this)[i]);
    6     }
    7 }; 

    singleton的初始化实现

    local static 能够确保在调用时已经初始化。但是仍然不能确保线程安全。所有non-const的static对象,无论它是local还是non-local,在多线程环境下都会有问题。解决的方法就是在一开始的单线程环境下,手动调用这些函数实现初始化。这样可以避免与初始化有关的race conditions。

    1 class Test {
    2 public:
    3    Test& test() {
    4         static Test t;
    5         return t;
    6     } 
    7 };    

    这种方式仅适用于创建对象;明确是惟一对象时才可使用。 

    virtual

    多态的析构函数,设成virtual,这样在delete的时候才会释放子类的所有资源。

    1 class Parent {
    2 public:
    3     virtual ~Parent() {...}  
    4 };
    5 class Child1: public Parent {};
    6 Parent* p = new Child1;
    7 delete p;

    多态的析构函数必须是virtual。

    在析构函数里,尽量不要调用会发生异常的操作。如果有,也要提供额外的接口,让用户有机会自己调用处理。

    不要在构造函数或析构函数里调用virtual 函数。此时调用virtual函数,调用的是base类的版本。

    对于自我实现的类,尽量以pass-by-reference-to-const替换pass-by-value;对于内置类型,以及stl的迭代器和函数对象,尽量以pass-by-value。

    调用父类函数:

    1 class child1 : public Parent {
    2 public:
    3     virtual void print() {
    4         Parent::print();
    5     }
    6 };

    同名遮掩

     1 class Parent {
     2 public:
     3     void t1(int) {}
     4     void t1() {}
     5 };
     6 class Child1 : public Parent {
     7 public:
     8     void t1() {}
     9 };
    10 class Child2 : public Parent {
    11 public:
    12     using Parent::t1;
    13     void t1() {}
    14 };
    15 Child1 c1;
    16 c1.t1(5); // error 
    17 Child2 c2;
    18 c2.t1(5); // right

    不使用using,父类的重载函数会被子类所遮掩,看不到了。 

    • 如果派生类的函数与基类的函数同名,但是参数不同。此时,若基类无virtual关键字,基类的函数将被隐藏。(注意别与重载混淆,虽然函数名相同参数不同应称之为重载,但这里不能理解为重载,因为派生类和基类不在同一名字空间作用域内。这里理解为隐藏)
    • 如果派生类的函数与基类的函数同名,但是参数不同。此时,若基类有virtual关键字,基类的函数将被隐式继承到派生类的vtable中。此时派生类 vtable中的函数指向基类版本的函数地址。同时这个新的函数版本添加到派生类中,作为派生类的重载版本。但在基类指针实现多态调用函数方法时,这个新的派生类函数版本将会被隐藏。
    • 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏。(注意别与覆盖混淆,这里理解为隐藏)。
    • 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数有virtual关键字。此时,基类的函数不会被“隐藏”。(在这里,你要理解为覆盖哦^_^)

    template

    嵌套的类型名称,是需要在修饰符前加上typename的。

    1 template<typename C>
    2 void print(const C& c) {
    3     typename C::const_iterator iter(c.begin());
    4     /* ... */
    5 }

    这里只能用typename,不能用class。

    编译细节

     1 class A {
     2 public:
     3     void print() {cout << "a" << endl;}
     4 };
     5 
     6 int main(int argc, char** argv) {
     7     A* pa = 0;
     8     pa->print();
     9 
    10     return 0;
    11 }

    这样的代码会core吗?不会,其实编译之后相当于print(A* this), 这时传入一个0,函数里面没有用,所以不会发生段错误。

    类型转换

    dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
    在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
    在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

    dynamic_cast运算符可以在执行期决定真正的类型。如果 downcast 是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果 downcast 不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象)。

    dynamic_cast要求父类必须有虚函数。static_cast会在编译期就告诉你能不能转。

    class D {
    };
    class E: public D {
    };
    int main(int argc, char** argv) {
        D* d = new E();
        E* e = static_cast<E*>(d);
        E* f = dynamic_cast<E*>(d);
        return 0;
    }

    这里D没有一个虚函数,所以会输出:

    1 t.cpp: In function ‘int main(int, char**)’:
    2 t.cpp:40:27: error: cannot dynamic_cast ‘d’ (of type ‘class D*’) to type ‘class E*’ (source type is not polymorphic)
    3   E* f = dynamic_cast<E*>(d);

    如果:

    1 int main(int argc, char** argv) {
    2     D* d = new D();
    3     E* e = static_cast<E*>(d);
    4     E* f = dynamic_cast<E*>(d);
    5     cout << (f ? "addr" : "null") << endl;
    6     return 0;
    7 }

    从D转到E,这样dynamic_cast会返回null。

    如果:

    int main(int argc, char** argv) {
        E* e = new E();
        D* d = static_cast<D*>(e);
        D* f = dynamic_cast<D*>(e);
        cout << d << endl;
        cout << f << endl;
        cout << (f ? "addr" : "null") << endl;
        return 0;
    }

    从E上行转D的话,static_cast和dynamic_cast是一样的,打印出来的地址都是一样的。 

    delete和delete[]

    正确的应该是new和delete对应,new[]和delete[]对应。

    1 E* e = new E();
    2 delete e;
    3 D* d = new D[10];
    4 delete[] d;

     如果,

    1 E* e = new E();
    2 delete[] e;

    编译同样通过了,但是也是会core dump。

    如果,

    1 D* d = new D[10];
    2 delete d;

    编译是通过了,但是会报错,指针出错。

    *** Error in `./t': munmap_chunk(): invalid pointer: 0x0000000001e8c038 ***
    Aborted (core dumped)
  • 相关阅读:
    CentOS75 安装 telnet 进行使用.
    Windows 创建计划任务 实现自动同步文件.
    qemu-img.exe 工具 简介
    中建项目环境迁移说明
    服务器内存最大大小限制
    bzip2 以及 tar 压缩/解压缩/.打包等工具软件
    Ubuntu18.04 安装后的简单实用设置[未完成]
    oracle 启动监听报错TNS-12547: TNS:lost contact
    Linux审计sudo
    OPENVAS运行
  • 原文地址:https://www.cnblogs.com/linyx/p/3984487.html
Copyright © 2011-2022 走看看