Effective C++学习第六天

条款18:让接口更容易被正确使用,不易被误用

          设计接口的原则:正确性、高效性、封装性、维护性、延展性以及协议的一致性;

          设计原则:1)导入新类型来预防很多客户端的错误,多使用系统类型(type system(划分为多个explicit函数),如果某个变量的可选情况有限,可以通过static函数来替代对象,表示某个特定的对象,并将对象的构造函数定义成private来防止新的对象生成,具体代码如下:

class  month{

public:

           static month Jan( ) {return month(1);}//以函数替代对象,来代替某个特定的月份

          ...

private:

         explicit month(int m);

};

 date(month::Jan( ));//调用

            2)限制类型内,什么事情可以做,什么事情不可以做,通常加上限制const;

            3)让你的types的行为和内置types行为一致,提供行为一致的接口;

            4)使用智能指针来管理系统资源,可以避免内存泄露(注意智能指针的管理方式和管理条件(单个对象),并用自定义的deleter(资源释放函数)绑定在智能指针上,deteler(可缺省)),代码如下:

 std::tr1::shared_ptr<investment>pinv(static_cast<investment*>(0),getridofinvestment);

                                                                      //shared_ptr第一个参数必须要求是一个指针,一个空指针shared_ptr

                                                                                            //getridofinvestment为删除器

这样的初始化不是最优的:先把pinv初始化为nullptr后对其进行赋值操作(相当于初始化过程是多余的,如果知道初始指针的话,直接用初始指针来初始化pinv才是最优的);

       使用智能指针的另外一个好的特性,它会自动使用它的各一个指针专属的删除器,因而消除一个潜在的客户错误(cross-DLL problem),即;对象在动态连接程序库(DLL)中被new创建,却在另外一个DLL内被delete,这一类称为“跨DLL之new/delete成对运用”会导致运行期错误,但是智能指针没有这个问题,它缺省的删除器来自于智能指针诞生的那个delete;

      Boost的shared_ptr指针是原始指针(raw pointer)的两倍大,以动态分配内存作为簿记用途和“删除器之专属数据”,以vrtual形式调用删除器,并以多线程程序修改引用次数时承受线程同步化的额外开销,但这些额外的执行成本并不显著,但是在降低客户错误的方面却起到了很好的效果;

条款19:设计class犹如设计type

            在设计一个新的class的时候,就像设计一个新的type,在设计一个高效的class的时候,应该考虑的问题有:

              1)对象如何被创建和销毁;

              2)对象初始化和赋值之间有什么差别;

              3)新对象在传值和传引用方面的考虑;

              4)对于成员变量引申的成员函数需要进行的错误检查工作及函数抛出异常及异常处理;

              5)考虑类型的继承和被继承情况;

              6)类型需要做什么样的转换,编写对应的类型转换函数;

             7)什么函数应该是成员函数,什么不是;

              8)什么函数应该定义为private;9)成员变量的属性;

              9)保证效率、异常安全性以及资源利用(多任务锁定和动态内存);

             10)一般化你的类型,考虑定义一个模板类;

             11)是否需要定义类,可以在别人类的基础上添加一个或者多个non-member函数或者template;

条款20:宁以pass-by-reference-to-const替换pass-by-value

         缺省情况下C++以by-value方式传递对象到函数,函数参数都是以实际实参的副本为初值,而调用端所获得的也是函数返回值的一个附件,这些副本由对象的copy构造函数产生,使得pass-by-value称为一个函数操作,如果有很多成员变量的时候;

         如果采用传递引用的方式,没有任何的构造函数或者析构函数调用,因为没有任何的对象被创建;除此之外,以传引用的方式传递参数可以避免slicing(对象切割)的问题,即当一个对derived class对象以by value的方式传递并视为一个base class对象,base class的拷贝构造函数被调用,但是derived class区别于base class的部分会被切割掉,而只留下一个base class;

        避免是slicing(切割)问题的方法,就是采用by-reference-to-const的方式传递,原因是因为reference往往是以指针的形式实现,pass by reference通常意味着传递的是指针;

       一般而言,对于内置类型、STL的迭代器和函数对象,采用传值比较方便,其他最好使用传引用;

条款21:必须返回对象时,别妄想返回其reference

          对于reference只是一个名称,代表某个既有对象。任何时候看到一个reference声明式,应该去了解它的另外一个名称是什么?

        分析代码如下:

//返回引用指向栈上的变量

错误方法1.const rational& operator*(const rational&lhs,const rational &rhs){

         rational result(lhs.n*rhs.n,rhs.d*rhs.d);//试图返回一个栈空间变量为引用

          return result;                                   //对象在函数调用结束后被销毁,引用指向一个错误的对象;

}

//返回引用指向堆上的变量

错误方法2.const rational& operator*(const rational&lhs,const rational &rhs){

         rational *result=new rational(lhs.n*rhs.n,rhs.d*rhs.d);//如何对这个new内存进行管理

          return *result;             //没办法取得reference背后的指针,如何a*b*c时更容易出错;

}

//返回引用指向一个函数内部的static rational对象

错误方法3.const rational& operator*(const rational&lhs,const rational &rhs){

         static rational result;    //staitc对象会导致多线程安全性

         result=rational temp(lhs.n*rhs.n,rhs.d*rhs.d);//static公用问题会带来错误的影响;

          return result;            

}

rational a,b,c,d; if(a*b==c*d)//if里面的数据一直为真,不管其他的怎么取值,因为static原因和返回是引用的原因

正确代码:inline const rational operator*(const rational&lhs,const rational &rhs){

            rational result(lhs.n*rhs.n,rhs.d*rhs.d);//虽然会有拷贝构造和析构的成本,但是对于安全性考虑却是一个很好的做法;

}

结论:不要返回pointer或者reference指向一个local stack对象(见错误方法1),或返回一个reference指向一个heap-allocated对象9见错误方法2),或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象(见错误方法3)

条款22:将成员变量声明为private

          如果成员变量不是public,客户能够访问对象的方式就是通过成员变量函数,如果public接口内的每样东西都是函数,那么可以避免客户在调用class成员时考虑是否需要使用小括号;如果你通过函数访问成员变量,那么你以后你改变成员变量时,class客户却完全不知道class 内部发生了变化;

        将成员变量隐藏在函数接口的背后没可以为“所有可能的实现”提供弹性;你对客户隐藏成员变量(封装性),你可以确定class的约束条件得到维护,因为只有成员函数可以影响它们;

        某个东西的封装性与“当其内容改变时可能造成的代码破坏量”成反比,改变就是从class中移除它;

       一旦你声明成员变量为public或者protected时,就很难改变这个成员变量涉及的一切,有太多的代码需要重写、重新测试、重新编写、重新编译;

       结论:将成员变量声明为private,这可以赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并为class提供充分的实现弹性;

         

           

猜你喜欢

转载自blog.csdn.net/xx18030637774/article/details/80800899