C/C++ 高质量编程
规则
for (i=0; i<10; i++)
{
, }
独占一行
*
应该紧跟变量名,即使是指针
S_
开头
g_
开头
m_
开头
if()
中的等于判断表达式应该将常量写在前面。 if (NULL == P)
。
for (i=0; i<N; i++)
。
default: break
结尾。
C++
中常量的定义不用#define
,推荐const变量。
const float DIAMETER = 2*RADIUS;
。
const &
来修饰,减少临时对象的构造和析构带来的成本。
int printf(const char *format, ...);
char *strcpy(char *dest, const char *src);
引用和指针的区别
- 引用被创建的同时必须初始化
- 不能有NULL引用,引用必须有存储单元
- 引用一旦初始化就不能更改引用的关系
引用传递在函数的参数和返回值上提高了效率。
野指针
野指针的成因:
指针指向已经释放或者访问受限的内存区域【本质】。
指针没有被初始化。
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); // 缺省的赋值函数
“缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量 ,这两个函数注定将出错。