[笔记]Effective C++改善程序与设计的55个具体做法_第五章 实现

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

实际上就是代码规范中在使用的地方进行定义的另一种说法。目的是为了改善程序运行效率。

条款 27:尽量少做转型动作。

旧式转型:

T(expression) // 将expression转型为T
(expression)T  // 将expression转型为T

两种方式无差别,C风格。

C++新式转型:

const_cast<T>(expression)
dynamic_cast<T>(expression)
reinterpret_cast<T>(expression)
static_cast<T>(expression)

各种类型转型的目的:

1. const_cast

将对象的常量属性移除,不过它不能改变对象本身的常量性,仅仅是去掉了类型的常量修饰。

const int x = 10;
int* nonConstPtr = const_cast<int*>(&x); // 去掉const属性
// *nonConstPtr = 20; // 对原本的常量对象进行修改会引发未定义行为

void print(int* num) { std::cout << *num; }
print(const_cast<int*>(&x)); // 把const int*转换为int*以便调用函数

使用提醒

  • 不要用它去修改真正的常量对象,不然会导致未定义行为。
  • 它的主要用途是在函数重载时处理const参数。

2. dynamic_cast

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

它依赖于运行时类型信息(RTTI)。要是转换失败,对于指针会返回nullptr,对于引用则会抛出std::bad_cast异常。

class Base { virtual void func() {} }; // 得有虚函数才能启用RTTI
class Derived : public Base {};

Base* basePtr = new Derived;
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 转换成功

Base* basePtr2 = new Base;
Derived* derivedPtr2 = dynamic_cast<Derived*>(basePtr2); // 转换失败,返回nullptr

使用提醒

  • 只有类中包含虚函数时,才能使用dynamic_cast
  • 由于要进行运行时检查,所以会带来一定的性能开销。

3. reinterpret_cast

意图执行低级转型,实际动作及结果可能取决于编译器,这也就表示它不可移植。例如将一个point to int转型为int。

int num = 42;
int* ptr = &num;
long long addr = reinterpret_cast<long long>(ptr); // 把指针转换为整数

char* charPtr = reinterpret_cast<char*>(ptr); // 把int*转换为char*

使用提醒

  • 使用reinterpret_cast得到的结果是与具体实现相关的,很可能不具备可移植性。
  • 要避免使用这种转换,除非是在进行底层编程,比如操作硬件时。

4. static_cast

用来强迫隐式转换(implicit conversions),例如将non-cast对象转换为const对象,或将int转换为double等等。它也可以用来执行上述多种转换的反向转换,例如将void *指针转换为typed指针,将pointer-to-base转为pointer-to-derived。

它主要用于进行良性转换,像基本数据类型之间的转换、子类到父类的向上转型等。不过,这种转换在运行时不会进行类型检查,所以需要你自己确保转换是安全的。

// 基本数据类型转换
double d = 3.14;
int i = static_cast<int>(d); // 把double类型转换为int类型,结果是3

// 子类到父类的向上转型(安全操作)
class Base {};
class Derived : public Base {};
Derived derived;
Base* basePtr = static_cast<Base*>(&derived); // 这种转换是安全的

使用提醒

  • 不能用它来移除const属性,这得用const_cast
  • 进行父类到子类的向下转型时,要保证对象实际上就是子类类型,否则会引发未定义行为。

任何一个类型转换(不论是通过转型操作而进行的显示转换,或通过编译器完成的隐式转换)往往真的灵编译器编译出运行期间执行的码。

最后建议:

建议优先使用 C++ 的新式转换,因为它们的意图更加明确,也更容易进行代码审查。

  • 当需要进行常规转换时,优先考虑使用static_cast
  • 在继承体系中进行安全的向下转型,要使用dynamic_cast
  • 去掉const属性,就用const_cast
  • 进行底层的强制类型转换,才使用reinterpret_cast,但要谨慎使用。

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

请记住

避免返回handles(包括references、指针、迭代器)指向对象内部。

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

请记住

  • 异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构破坏。这样的函数分为三种可能得保证:基本型、强类型、不抛异常型。

  • "强烈保证"往往能够以copy-and-swap实现出来,但“强烈保证”并非对所有函数都可实现或具备现实意义。

  • 函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者。

条款 30:透彻了解 inlining 的里里外外。

请记住

  • 将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级(binary upgradability)更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。

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

条款 31:将文件间的编译依赖依存关系降至最低。

请记住

  • 支持“编译依存性”最小化的一般构想是:相依于声明式,不要相依于定义式。基于此构想的两个手段是handle classes和interface classes.
  • 程序库头文件应该以“完全且仅有声明式”(full and declaration-only forms)的形式存在。这种做法不论是否涉及templates都适用。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇