第五章:实现(一)

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

只要你定义了一个变量而其类型带有一个构造函数或析构函数,那么当程序控制流到达这个变量定义式时,你便得承受构造成本;当这个变量离开其作用域时,你便得承受析构成本。即使这个变量最终并未被使用,仍需耗费这些成本,所以你一个尽可能避免这种情形。

V1:

std::string encryptPasswd(const std::string& password)
{
    using namespace std;
    string encrypted;
    
    if(password.length() < MinmumPasswordLength)
    {
        throw logic_error("Password is too short");
    }
    ... //必要动作,能将一个加密后的密码置入变量encrypted内
    
    return encrypted;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

如果encryptPasswd在检查密码长度时,抛出异常,就不会走到后续加密动作,应该延迟encrypted的定义。

V2:

std::string encryptPasswd(const std::string& password)
{
    using namespace std;
    
    if(password.length() < MinmumPasswordLength)
    {
        throw logic_error("Password is too short");
    }
    string encrypted;
    ... //必要动作,能将一个加密后的密码置入变量encrypted内
    
    return encrypted;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

encrypted虽获定义却无任何实参作为初值,因此会调用default构造函数。

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

V3:

std::string encryptPasswd(const std::string& password)
{
    
    ... //检查length,如前
    std::string encrypted(password); //通过copy构造函数,定义并初始化
    encrypt(encrypted);  //加密函数
    return encrypted;
}
1
2
3
4
5
6
7
8

如果变量在循环内使用:

//方法A:定义于循环外
Widget w;
for(int i=0;i<n;i++)
{
    w = 取决于i的某个值;
    ...
}

//方法B:定义于循环内
Widget w;
for(int i=0;i<n;i++)
{
    Widget w(取决于i的某个值);
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 做法A:1个构造函数+1个析构函数+n个赋值操作
  • 做法B:n个构造函数+n个析构函数
  1. 知道赋值成本比“构造和析构”成本低;
  2. 正在处理代码中效率高敏感的部分。

满足以上两点,可以选择做法A,否则选择做法B

尽量少做转型动作

C++提供四种新式转型:

const_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)
static_cast<T>(expression)
1
2
3
4
class Window
{
public:
    virtual void onResize() {...}
    ...
};

class SpecicalWindow:public Window
{
public:
    virtual void onResize() 
    {
        static_cast<Window>(*this).onResize();//调用的是*this对象之base class成分的暂时副本身上的onResize函数。
    }
    ...
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

应该这样写:

class SpecicalWindow:public Window
{
public:
    virtual void onResize() 
    {
        Window::onResize();//调用Window::onResize作用于*this身上
        ...
    }
    ...
};
1
2
3
4
5
6
7
8
9
10

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

reference、指针和迭代器都是所谓的handles(号码牌,用来取得某个对象),而返回一个“代表对象内部的数据”的handle,随之而来的便是“降低对象封装性”的风险。

Method& getMethod() const
{
    return method;
}
1
2
3
4

//getMethod()返回的Method可以修改,从而导致封装性被破坏。

进一步改进:

const Method& getMethod() const
{
    return method;
}
1
2
3
4

有可能存在dangling handles(空悬的号码牌):这种handles所指东西不复存在。

const Rectangle boundingBox(const GUIobject& obj);
...
GUIobject* pgo;
const Ponit* pUpperLeft = &(boundingBox(*pgo).upperLeft());
//reference指向了一个临时变量temp的内部成分,但是temp在boundingBox函数返回时被销毁,导致pUpperLeft指向对象不存在
1
2
3
4
5