读 C++ 编程风格后感
书籍连接: http://book.douban.com/subject/1967180/
抽象
在使用公有继承来实现一种关系时,我们通常要解决的两个中有问题?
- 在继承层次中,那些是共有属性?
- 各个派生类的区别是什么?
在一个类中我么要决定类中到底是使用数据成员还是成员函数来表示一个特性时,我们应该首先该考虑这个特性是有属性值描述特性还是有行为来描述?
- 如果是一个属性值,那么用数据成员表示
- 如果是一个行为(一种操作或算法),那么用成员函数表示
派生类和基类是否有同样的行为?
- 如果派生类表现出不同的行为,那么就应该使用虚函数
- 如果所有的派生类都表现出相同的行为,那么就可以共享一个基类的非虚函数
在抽象时候应该注意什么?
- 将共同的抽象(属性和方法)提取出来并放到基类中
更好的抽象不仅改善了程序的框架,同时也减少了程序的代码量,通常小程序要比大程序更加易于阅读与修改,当然代码量的减少并不是我们的目标,因为用最少行数的代码来编写程序往往并不会产生良好的解决方案,然后优良的代码却总是比拙劣的代码更短,这是因为在好的设计中通常使用了正确的抽象
一般过于特化是不好的抽象,因为每个类被具体到只能描述一种对象.通常一个类应该能描述一组对象的共性,而不仅仅只是单个对象 - 一个类应该能够描述一组对象
- 如果派生类之间的区别在于属性,那么用数据成员来表示,如果派生类区别在于行为,用虚函数来表示
如果用虚函数表示属性之间的不同.使用数据来表示属性之间的区别,要比使用虚函数的表示更易于编程.如果在所欲的对象中都存在着共同的抽象时,那么继承层次越简单,形成的模型就越精确
由于继承和虚函数的功能强大,并且是一种新技术,隐藏每个程序员都不愿意错咯每一次能使用这些技术的机会,然而,并不是每一个问题都需要虚函数才能解决的.使用数据成员来表示对象在属性上的确保也是一种不错的方法,程序员不应该只是因为C++提供了更为复杂的编程技术而放弃那些标准可靠的编程技术 - 如果有公共继承来派生派生类,那么这个派生类应该是其基类的特化
- 多态并不是所有程序设计解决的方法
一致性
什么是一致性?
- 从外部看,当客户代码通过类的公有接口来操作对象时,对象的行为必须是一致的
- 从内部看,类的实现在对象的状态上必须是一致的,在数据成员之间的类型不变形关系是一致的一种表现形式,而用于动态内存管理的一致性策略则是另一种表现形式
如何能提高程序对数据的一致性?
- 构造函数应该使得对象处于明确定义的状态(对类中数据成员赋初值)
- 我们应该考虑使用默认新参的形式来代替函数重载(因参数个数不同的重载)
- 用一致的方式来定义对象的状态---这需要识别出类不变性
对于每个类,我么可以写一组类的不变性的条件,在每个对象的生存周期,这些条件都是不边的(例如 string 中的 lenth) - 类的接口定义应该是一致的---避免产生困惑
对对应的接口的操作方式应该保持一致(比如string对内存的分配,有两种方式,一种是静态分配,一种是动态分配,静态分配的优点是效率高,而动态分配的优点是放置数组越界,但是在string类中要保持对内存方法的一致性不然容易产生困惑) - 对于每个new操作,都要有相应的delete操作
- 避免对从不使用的状态进行计算和存储,这样容易保持数据的一致性
不必要的继承
写继承时要注意什么?
- 我们要找出简单抽象,避免复杂的继承关系.尽量是类能表示一类事物,而不是特化的一种事物,减少不必要的继承
- 我么要识别对实现的继承来可以使用私有基类或者使用成员对象(更好)
私有继承不但能防止基类的公有接口为派生类公有接口的一部分,还能防止基类的指针或者引用指向派生类对象
类的类型转换安全么?
类型转换是不安全的,为了避免这种潜在危险,在编写程序的时候最好不要使用类的类型转换
继承作用域准则?
class Base{ public: void f(float); void g(float); } class Derived:public Base{ public: void f(int); }
编译器在搜索函数g的定义时,将首先在派生类中搜索,如果没有找到则到继续在基类中搜索.在使用派生类的对象时,我们可以通过名字g来调用Base::g但是不能通过名字f简单调用Base::f但是可以通过函数的网球解析名字来调用函数,Base::f()
虚函数
写虚函数析构函数是应该注意哪些?
- 如果公有基类中没有定义虚析构函数,那么在所有派生类或者派生类的数据成员中都应该没有定义析构函数
- 通常情况下,公有基类的析构函数应该被声明为虚函数
与继承相关基类的析构函数没有声明成为虚函数,如果我们动态创建了一个派生类对象,然后用基类的指针指向派生类对象,然后通过这个基类的指针来释放派生类对象,这时析构函数只会调用基类的派生类对象而不会调用派生类的析构函数,析构函数的行为与其他成员函数行为一样,哪类析构函数调用取决于函数指针类型,但是析构函数声明为虚函数时,与其他虚函数一样,在用基类指针释放派生类对象时,调用的是派生类的析构函数,这时,哪类析构函数调用取决于指针指向对象的类型 - 没有哪个类是完美的,但是过窄的设计优于过宽的设计
运算符重载
注意: 我们应该编写出清晰的程序---而不是展现自己小聪明的程序
写运算符重载需要注意什么?
- 重载运算符必须能够与其他运算符进行正确交互
如果重载一个可交换左右两边操作数的运算符(例子: x+y = y+x),如果来个操作数类型相同则不需要用到重载,如果连个操作数类型不同则还需用到重载 - 我们要保持重载的一致性
- 我们在重载运算符的行为是一致的.
- 在定义operator=时,我们要注意x=x这种情况.

浙公网安备 33010602011771号