1 让自己习惯C++

来源:互联网 发布:孩子网络借贷怎么办 编辑:程序博客网 时间:2024/06/05 04:25
2017年8月10日 15:05:39



1. 视C++为一个语言联邦
    C++已经是个多重范型编程语言,一个同时支持过程形式、面向对象形式、
    泛型形式、元编程形式的语言。

    C++主要的次语言:
        C : C++的基础,但C有局限:没有模版、没有异常、没有重载……
        Object-Oriented C++: class、封装、继承、多态、virtual函数(动态绑定)……
        Template C++: C++的泛型编程部分
        STL : STL是一个template程序库。它读容器,迭代器,算法以及函数对象的规约有极佳的紧密配合与协调。

    对于内置类型来说:pass-by-value 比 pass-by-reference高效
    对于用户自定义类型class来说,pass-by-reference-to-const 往往更好

    C++高效编程守则视情况而变化,取决于使用C++的哪一部分。


2. 尽量以 const, enum, inline 替代 #define
    #define 的内容 可能没有被编译器看到,就被预处理器拿走,可能并没有进入记号表

    当以常量替换 #define 时,有两种特殊情况:
        (1)定义常量指针:
             const char * const authorNmae = "Zhangyan";
             或者 const std::string authorNmae = "Zhangyan";

        (2)class 专属常量
             类中的常量,为了保证作用域限制于class内部,必须加const
                          为了保证此常量最多只有一份实体,必须成为static成员
                     即:static const int Num = 6;
                     注意:该式子只是声明。但 static const 变量不需要实例,就可以使用(其他变量都要实例,才能使用,
                           且在声明时,不能赋初值)。
                           如果编译器错误处理,一定要实例,那么只需要: const int A::Num;
                           把这个式子放在实现文件(.c文件)。因为class内部已经声明时,获得初值,所以定义时不可以再赋值。

    #define 并不重视作用域,不提供任何封装性,所以不能将class专属常量定义为宏。而const提供封装性。

    如果编译器不允许 static const 在类内赋初值,而又要用该常量,来作为数组大小,此时可以用enum

    对enum和define取地址是不合法的,而对const取地址合法

    enum和define一样,不会导致非必要的内存分配。而优秀的编译器,同样不会为“整型const对象”设定另外的存储空间

    对于宏函数来说,可以用inline函数代替。


3. 尽可能使用const

    某些东西声明为 const 可以帮助编译器侦测出错误用法。

    对于迭代器来说:
        const std::vector<int>::iterator iter = vec.begin();
        此时,相当于 char * const,即,iter的指向不能发生改变。

        std::vector<int>:: const_iterator citer = vec.begin();
        此时,相当于 char const *,即:citer指向的对象的值,不能发生改变

    对于函数返回值来说:
        const 往往可以降低因客户错误而造成的意外,而不至于放弃安全性和高效性
            例如: class A
                    {
                        const A operator = (A &a, A &B);
                    };
                如果不加 const,那么 A a,b,c;    (a * b) = c;    将调用成功

    const 成员函数:
        目的:让该 const 成员函数可作用于 const对象身上。
        意义:(1) 使得 class 接口变得容易理解
              (2) 只能操作 const 成员函数。(因为,操作非const成员函数,可能会改变成员变量,
                                                  为了避免出现在这种情况,直接操作非const成员函数禁止)。

        const 可以作为重载条件

        const 成员函数不可以更改对象内任何 non-static 成员变量,如果需要改变,可以在该变量定义前,加 mutable

    在const 和 non-const 成员函数中避免重复
        因为这两者的内容,往往一致,为了避免代码重复,就是用 non-const 来调用 const 成员函数。
        char &opeartor[] (std::size_t position)
        {
            return
                const_cast<char &>(static_cast<const TextBlock&>(*this)[position]);
        }
        如果只是调用operator[],那么会递归,栈溢出,因此需要指明调用 const opeartor[]

        如果想用 const 成员函数 调用 non-const 成员函数,是错误的,因为对象可能因此改动。


4. 确定对象被使用前已经被初始化
    永远在使用对象之前先将它初始化。对于无任何成员的内置类型,必须手工完成此事。

    初始化的责任在构造函数身上,因此:确保每一个构造函数都将对象的每一个成员初始化。

    C++规定,对象的成员变量的初始化发生在进入构造函数本体之前。
    因此,成员变量的初始化,应该在初始化列表完成,而不是在构造函数内,用 ‘=’来赋值。
        (对于赋值操作来说,大多数类型会先调用构造函数,发现在初始化列表没有,然后再调用赋值运算符)
    即使是一个无参的构造函数,也应该调用初始化列表,来初始化成员变量:
            A::A() : index()
            { }
        编译器会为用户自定义类型之成员变量自动调用默认构造函数。

    const 和 reference 成员变量,一定要使用初始化成员列表。

    简单的方法是:都使用成员列表初始化。
        但是大量在初始化列表,会导致重复。此时可以选择“赋值表现和初始化一样好”的成员变量,将其初始化移到,
        一个private成员函数,然后所有的构造函数,再调用这个函数。

    C++的初始化顺序:
        (1)基类早于派生类初始化
        (2)class 的成员变量总是以其声明次序被初始化,而不是初始化成员列表中的顺序。

    对于:跨编译单元之间初始化次序的问题
        C++对于“定义于不同编译单元内的non-local static 对象”的初始化次序并没有明确的定义。
        所以当不同的编译单元,相互依赖,又要确保构造顺序时,最好的方法是加强程序设计。
        比如:使用类似单例模式的行为,可以创造一个 static 成员函数,返回一个已经初始化好的对象,
              其他类需要使用时,直接调用这个函数即可。
              但是 内涵static对象 可能造成多线程系统中带有不确定性。 解决的方法是:在单线程启动时,
              就手工调用所有static函数,进行初始化。
        (编译单元:多个.c文件)

原创粉丝点击