C/C++ 高质量编程

规则

  • 二元操作符前后需要空格
  • 一元操作符前后不加空格
  • 循环语句等可以适当紧凑,比如:for (i=0; i<10; i++)
  • {, }独占一行
  • 修饰符*应该紧跟变量名,即使是指针
  • 注释应该写在代码的上方或者右方
  • 多重嵌套,结束时加上提示性注释
  • 类的排版方式:以数据为中心,数据在前,函数在后;以接口为中心,函数在前,数据在后。
  • 命名风格和OS, development tool相适应。大小写 — windows,下划线 — Unix.
  • 类和函数名大写字符开头
  • 变量和参数用小写字符开头
  • 常量全大写
  • 静态变量S_开头
  • 全局变量g_开头
  • 类的数据成员m_开头
  • 函数库的变量加特有前缀,openGL — gl
  • 真 – 非0,假 – 0,故if语句条件判断中不能认为0是false, 1是true。
  • if()中的等于判断表达式应该将常量写在前面。 if (NULL == P)
  • 多重循环,尽量将长循环放在里面,减少CPU切换循环的开销。
  • 循环语句采用半开半封闭的语句结构。比如 for (i=0; i<N; i++)
  • switch语句末尾应该以default: break结尾。
  • C++中常量的定义不用#define,推荐const变量。
  • 关系常量定义,如:const float DIAMETER = 2*RADIUS;
  • 类中的const变量,全局而言是可变的,不同的对象可以有不同值的const变量。对象内的const变量是不变的。所以const变量只能在类的构造函数的初始化列表中被初始化。
  • enum枚举变量可在类中全局恒定不变。
  • 值传递的方式传递对象,对象最好用const &来修饰,减少临时对象的构造和析构带来的成本。
  • 带有不确定的参数函数,比如int printf(const char *format, ...);
  • 不加返回类型的函数被自动处理成整型返回函数。
  • EOF的定义值是负数,通常为-1。
  • char *strcpy(char *dest, const char *src);
  • 不要编写技巧性很高的代码
  • 不要设计面面俱到、非常灵活的数据结构。

    引用和指针的区别

    1. 引用被创建的同时必须初始化
    2. 不能有NULL引用,引用必须有存储单元
    3. 引用一旦初始化就不能更改引用的关系

    引用传递在函数的参数和返回值上提高了效率。

    野指针

    野指针的成因:
    指针指向已经释放或者访问受限的内存区域【本质】。
    指针没有被初始化。

    sizeof(p)

    char *p = NULL;sizeof(p) 等价于 sizeof(char *)
    C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。
    注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。示例中,不论数组a 的容量是多少,sizeof(a)始终等于sizeof(char *)。

  • char a[] = "hello world";
    char *p = a;
    cout<< sizeof(a) << endl; // 12 字节
    cout<< sizeof(p) << endl; // 4 字节
    
    void Func(char a[100])
    {
    cout<< sizeof(a) << endl; // 4 字节而不是100 字节
    }
  • 全局函数的调用

    在代码块中,为了区分局部函数,使用::来调用全局函数。 ::funcname()

    自动的类型转换

    数值没有类型,隐式转换可能产生问题。比如:0.5可能变成int, 可能变成float。

    函数的重载和覆盖

    关键识别点是参数的个数或类型。
    覆盖是指派生类函数覆盖基类函数,特征是:
    (1)不同的范围(分别位于派生类与基类);
    (2)函数名字相同;
    (3)参数相同;
    (4)基类函数必须有virtual 关键字。
    基类和派生类的函数没有重载、覆盖,对应的叫隐藏。

  • #include <iostream.h>
    class Base
    {
        public:
        virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
        void g(float x){ cout << "Base::g(float) " << x << endl; }
        void h(float x){ cout << "Base::h(float) " << x << endl; }
    };
    
    
    class Derived : public Base
    {
        public:
        virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
        void g(int x){ cout << "Derived::g(int) " << x << endl; }
        void h(float x){ cout << "Derived::h(float) " << x << endl; }
    };
    
    void main(void)
    {
        Derived d;
        Base *pb = &d;
        Derived *pd = &d;
        // Good : behavior depends solely on type of the object
        pb->f(3.14f); // Derived::f(float) 3.14
        pd->f(3.14f); // Derived::f(float) 3.14
        // Bad : behavior depends on type of the pointer
        pb->g(3.14f); // Base::g(float) 3.14
        pd->g(3.14f); // Derived::g(int) 3 (surprise!)
        // Bad : behavior depends on type of the pointer
        pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
        pd->h(3.14f); // Derived::h(float) 3.14
    }
  • 子类函数调用父类函数的关键字:Base::

  • class Derived : public Base
    {
        public:
        void f(char *str);
        void f(int x) { Base::f(x); }
    };
  • 函数参数的缺省值

    如果函数有多个参数,参数只能从后向前挨个儿缺省。
    函数参数的缺省值有减少函数参数的作用,可能让函数的调用产生二义性。

    内联函数和宏的比较

    内联函数具有安全检查和自动类型转化的功能,比宏更加安全。
    内联函数的关键字inline一定要放在函数定义之前,这和virtual是刚好相反的。

  • void Foo(int x, int y);
    inline void Foo(int x, int y) // inline 与函数定义体放在一起
    {
    ⋯
    }
  • 定义在类声明之中的成员函数将自动地成为内联函数

    断言

    断言是宏,不是函数

    构造函数

    每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。
    对于任意一个类A,如果不想编写上述函数,
    C++编译器将自动为A 产生四个缺省的函数,如

  • A(void); // 缺省的无参数构造函数
    A(const A &a); // 缺省的拷贝构造函数
    ~A(void); // 缺省的析构函数
    A & operate =(const A &a); // 缺省的赋值函数
  • “缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量 ,这两个函数注定将出错。

  • 分类: career

    发表评论

    电子邮件地址不会被公开。 必填项已用*标注