《Effective C++》条款26-31 实现

条款26 尽可能延后变量定义式的出现时间

总结:

尽可能延后变量定义式的出现,这样做可增加程序的清晰度并改善程序效率。

1、 错误的方式:

string func(const string& password){

    string temp;

    if(password.length()<Min){
      throw logic_error("Password is too short");
     }
     ...
     return temp;
}

如果丢出了异常,string temp的构造和析构成本都浪费了。

所以,应该把string temp放到throw后面。

2、更进一步

不应该只是延后变量的定义,而是应该尝试延后这份定义直到能够给他初值实参为止。

通过“”default构造函数构造出一个对象然后对他赋值“”,比,“”直接在构造时指定初值效率差“”。

string func(const string& password){

    if(password.length()<Min){
      throw logic_error("Password is too short");
     }
     // 直接给它初值实参
     string temp(password);

     return temp;
}

条款27 尽量少做转型动作

总结

1、如果可以,尽量避免转型,特别是在注重效率的代码避免dynamic_casts。

2、如果转型是必要的,试着将它隐藏于某个函数背后,客户随时可以调动该函数,而不需将转型放进他们自己的代码内。

3、宁可使用C++新式转型,不要使用旧式转型。

1、四种新式转型

const_cast<T>(expression)      将对象的常量性const 移除,将const转为non-const

dynamic_cast<T>(expression)  // 安全向下转型,决定某对象是否归属继承体系中的某个类型

reinterpret_cast<T>(expression) // 执行低级转型

static_cast<T>(expression)  // 强迫隐式转换,将non-const转为const

2、

class Base  {};

class Derived:public Base  {};

Derived d; // 派生类对象
Base* pb=&d; // 基类指针指向派生类对象

基类指针指向派生类对象,隐喻的将Derived* 转换为 Base*。

单一对象可能拥有一个以上的地址(Base*指向它的地址 和  Derived*指向它的地址)

3、拿掉转型动作

class Derived:public Base{

   public:
     virtual void func(){
      Base::func();//  直接调用Base类的成员函数
    }

}

4、dynamic_cast

之所以需要dynamic_cast,因为你想在一个你认定为derived class对象身上执行derived class 操作函数,但你的手上却只有一个“指向base”的pointer活reference ,你只能靠他们来处理对象。 

安全向下转型,决定某对象是否归属继承体系中的某个类型。

两种方式:

1、使用容器直接指向derived class 对象的指针,如此便消除了 通过base class 接口处理对象。

typedef vector<shared_ptr<Derived>>  VPSW;// 直接用派生类
VPSW bin;// 派生类vector的对象

for(VPSW::iterator iter=bin.begin();iter!=bin.end();++iter;){
    (*iter)->blink();
}

 2、base class内提供virtual函数做你想对各个派生类想做的事。

 

条款28 避免返回handles指向对象内部成分

避免返回handles(包括引用、指针、迭代器)指向对象内部。遵守这个条款可增加封装性。

 错误的行为:

class Retangle{
  public:
   Point& upperLeft() const {return pData->ulhc;}// 成员函数返回引用
}

 const Retangle rec();// const类型对象
 rec.upperLeft().setX(50); //const对象,但是成员函数返回引用即可以更改,bitwise constness

 const对象,但是成员函数返回引用即可以更改,bitwise constness问题

所以:应该避免返回引用指向对象内部。

解决方式:让成员函数返回值const

const Point& upperLeft() const {return pData->ulhc;}// 成员函数返回const引用

条款29 为“异常安全”而努力是值得的

 

条款30 透彻了解inling的里里外外

总结:

1、inlining在小型、被频繁调用的函数身上,提升程序速度。

2、不要只因为function templates出现在头文件,就将他们声明为inline。

(1)为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数。栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题

(2)建议:inline函数的定义放在头文件中,仅仅声明是没用的。inline 是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。

// 头文件
class A
{
    public:
    inline  void Foo(int x, int y){
    ... 
     };
}
 

一、inline只是对编译器的一个申请,不是强制命令

(1)隐喻提出(隐喻方式是将函数定义于class定义式内):

class Person { 
public: 
    ... 
    int age() const {return theAge;}//一个隐喻的inline申请 
    ... 
private: 
    int theAge; 
};

 (2)明确提出(明确申请inline函数的做法是在其定义式前加上关键字inline):

template<typename T> 
inline const T& std::max(const T& a, const T& b) { 
    return a < b? b: a; 
}

二、virtual函数+函数指针+构造析构函数 不能inline

大部分编译器拒绝将太过复杂(例如带有循环或递归)的函数inlining,而所有对virtual函数的调用也都会使inlining落空。因为virtual意味着“等待,直到运行期才确定调用哪个函数”,而inline意味“执行前,先将调用动作替换为被调用函数的本体”。如果编译器不知道该调用哪个函数,那肯定就没法inlining了!


编译器通常不对“通过函数指针而进行的调用”实施inlining。

inline void f(){…} //假设编译器有意愿inline“对f的调用”
void (*pf)() = f;
f();//这个调用将被inlined,因为是一个正常调用
pf();//这个调用或许不被inlined,因为通过指针达成

条款31 将文件间的编译依存关系降到最低

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值