c++ 准则

1. 没有面向对象也可以使用c++,因为c++=c+OO+template+stl,高效编程取决于使用c++的哪一部分特性

 

2.

尽量少用#define

a)const替换常量:

const double ration = 2.3;

const char* const name="jack";

const std::string name("jack");

class A{

static const int Num=8;---------声明

};

const int A::Num;-----定义

某些编译器不支持上述方法,可以使用下述方法

class A{

static const int Num;

};

const int A::Num=8;

b)枚举替换常量

class{

enum{RED=10};

}

c)内联替换函数形式的宏

template<typename T>

inline void test(const T&a, const T&b)

{};

 

3.

const char *p;--------p指向的内容不可变

char *const p;--------p本身不可变

void f2(WIdget const *w);---同const Widget *w

const std::vector<int>::iterator iter=vec.begin();---------itertor本身不可变

std::vector<int>::const_iterator iter = vec.begin();-------iterator指向的内容不可变

const Widget operator*(const Widget &a, const Widget &b);----------返回一个const

class A{

public:

const char& operator[](..) const-------调用者为const,返回也是const

const change() const;

mutable int a;----------尽管change函数调用者为const,但是由于mutable,则a允许改变。

int b;---------change中不允许改变b值,因为调用者为const

}

 

4. 为免除跨编译单元之初始化次序问题,用local static对象(例如函数内的static)替换non-local static对象(例如类,作用域,文件内的全局对象)

例如extern ClassA aa;

ClassB cb(aa.get());

这时候aa可能还没有初始化,存在不确定性,因此,需要定义本地static的classA对象,然后调用A的成员

 

5. 编译器一般会自动为类生成默认的构造函数,拷贝构造函数,赋值运算符,析构函数等。

如果想阻止默认的拷贝构造函数或者赋值运算符,可以将其定义为private成员函数,并且不予实现即可。

 

6. 多态性质的基类应该声明一个虚拟析构函数,如果类带有任何virtual函数,那么其析构函数应该是virtual的

如果类设计不是作为base class,或者不为具备多态性,那么不应该声明virtual 析构函数。

 

7. 析构函数不要抛出异常,如果析构函数调用了一个抛出异常的函数,那么应该捕获它,不能让它传播下去。

如果客户提供一个针对异常做出反应的函数,那么class应该提供一个普通函数执行该操作(而非在析构函数中)。

 

8. 在构造和析构期间不要调用virtual函数,因为这类调用从不下降至派生类。即,只执行基类的操作。与预期不符

 

9.

Widget &operator=(const Widget &rhs)

{

if(this == &rhs) return *this;----------如果自我赋值,则直接返回自身

return *this;----------返回指向当前对象的引用

}

 

10. 拷贝构造函数,赋值函数,必须要将右值的所有成员都赋值给当前对象,包括基类成员,一个不能少。

Widget &operator=(cosnt Widget &rhs)

{

...

BaseClass::operator=(rhs);

}

Widget(const Widget &rhs):BaseClass(rhs){...}

不要尝试在拷贝构造函数中调用赋值函数,反之亦然。可以提供一个common函数供两者调用

 

11.

为防止资源泄漏,情使用RAII对象(资源获得便是初始化时期),它们在构造函数中获得资源,在析构函数中释放资源。

std::shared_ptr<Mutext> mutextPtr(Mutext, unlock);

当Mutext引用计数为0时,调用释放器,这里为unlock函数。

 shared_ptr的get方法可以获得原始指针,或者通过->,*访问

隐式转换

operator FontHandle()const;

operator double()const;

 

12. new与delete,new[]与delete[]配对

processWidget(std::shared_ptr<Widget>(new Didget), priority());---可能泄漏,因为首先new Didget,然后priority如果出现异常,那么之前的Didget将无法释放.

解决办法,写成两行.

std::shared_ptr<Widget> pw(new Widget);

processWidget(pw, priority());

 

13. c++编译器底层,引用往往是以指针实现出来的。因此按引用传递意味着真正传递的是指针。

尽量以按引用传递替换按值传递。内置类型除外。STL的迭代器和函数对象除外。

 

14. 任何时候看到一个引用声明式,应该立刻问自己,它的另一个名称是什么?因为它一定是某物的另一个名称。

决不要返回指针或者引用指向一个local stack对象,或者返回引用指向一个堆对象,或返回引用或者指针指向一个local static对象而有可能同时需要多个这样的对象。

 

15. 宁可拿non-member,non-friend函数替换member函数。这样可以增加封装性,包裹弹性,机能扩充性。

因为成员函数会对成员变量发起访问的攻势。

 

16. result=oneHalf*2;------隐式转换

result=2*oneHalf;---不是隐式转换

只有当参数被列于参数列表内,这个参数才是隐式转换的类型转换的合格者。

如果要为某个函数的所有参数进行类型转换,这个函数必然是non-member,因为如果是member那么必然第一个参数是此函数对象。

 

17. class Widget{

public:

...

void swap(Widget &other)

{

using std::swap;

swap(pImpl, other.pImpl);

}

};

namespace std{

template<>

void swap<Widget>(Widget &a, Widget &b)-----------swap的特化版本,即显示具体化

{

a.swap(b);

}

}

如果提供一个member的swap,也应该提供一个non-member的swap来调用前者。

因为member的swap才可以访问类成员。

std是个特殊的命名空间,客户可以全特化std内的template,但不可以添加新的templates

 

18. 尽可能延后变量定义的出现,因为前面语句退出后,后面变量定义的成本消失。

 

19. 如果可以,避免转型。优先使用c++新式转型,即***_cast.

 

20. 避免返回handle指向对象内部。

 

21. 将大多数inlining限制在小型,被频繁调用的函数身上。

 

22. public继承意味着is-a,适用于base class身上的每一件事情也适用于派生类身上。因为每一个派生类对象也都是一个派生类对象。

virtual 函数意味着接口必须被继承

non-virtual函数意味着接口和实现都必须被继承。

 

23.

class Base{

public:

 virtual void mf1()=0;

 virtual void mf1(int);

 virtual void mf2();

 void mf3();

 void mf3(double);

};

class Derived:public Base{

public:

 using Base::mf1;

 using Base::mf3

 virtual void mf1();

 void mf3();

 void mf4();

}

这里由于Derived类重新实现了函数名mf1和mf3,因此Base的mf1和mf3都被遮盖,要想重见天日,必须using声明,这样Base的所有mf1和mf3都会被继承过来,如果派生类含有与之参数类型一样的,那么Base的将再次被遮盖。

Derived d;

int x;

d.mf1();------Derived::mf1(),base的mf1虚函数被重写为derived的mf1

d.mf1(x);------Base::mf1(int);,using声明使得可以使用,继承而来

d.mf2();-------Base::mf2();, 纯粹继承而来

d.mf3();---------Derived::mf3();,base的mf3因using指令被重见天日,但是derived含有参数与之一致的函数,因此base的被遮盖

d.mf3(x);-------Base::mf3(int); using声明使得可以使用,继承而来

如果继承base并加上重载函数,而你又希望重新定义或覆盖其中一部分,那么必须为那些原本会被遮盖的每个名称引入一个using声明,否则某些你希望继承的名称会被遮盖。

 

24. 继承而来的non-virtual函数绝对不要重新定义。

因为非虚函数被继承说明你想使用该方法,如果重新定义就矛盾了。

 

25. 有时候被继承的函数实现方式想被修改,而此函数又是非虚的,怎么处理?

答案是:使用NVI方法,非虚函数接口

class A

{

public:

 void draw()-------------non-virtual函数

{

doDraw();-----------调用一个virtual

}

virtual void doDraw() const=0;------真正的工作在此完成

}

Class B:public A

{

private:

virtual void doDraw()const;---------重写虚函数

}

 

26. has-a或“根据某物实现出”

is-a大家已经熟悉,即public继承

has-a大家也清楚,即一个类为另一个类的成员变量。两个类之间的关系为has-a

根据某物实现出:其实类似has-a,但是它是从实现角度出发,例如set是基于list实现的,只不过部分不同,例如不能重复。那么就可以在set类中增加一个list对象,然后set成员函数的实现大多直接调用list对象的方法直接实现。做少许修改。

private,protect继承也属于根据某物实现出的范畴,它比包含对象变量的复合方式级别低。但是当派生类需要访问保护基类的成员,或者需要重新定义继承而来的虚函数的时候这么设计师合理的。

 

27. 声明template参数时,前缀关键字class和typename可以互换。

但是标示嵌套从属类型名称时需要使用typename。

 

28. STL容器所使用的heap内存是由容器所拥有的分配器对象管理,不是被new和delete直接管理。

 

29.

void outOfMem()
{;}

std::set_new_handler(outOfMem);

后续new失败后会调用上面的函数。

 

30.

关于自定义new操作符,可以使用两种方法:

1. 定义global作用域内提供的operator new

void *operator new(std::size_t) throw(std::bad_alloc);//normal new

void *operator new(std::size_t, void *) throw();//placement new

void *operator new(std::size_t, const std::nothrow_t &) throw();//nothrow new,为兼容旧版本

2. 类内部重载operator new

同上。

此外,需要定义对等的operator delete操作符。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值