本文共 9409 字,大约阅读时间需要 31 分钟。
const
一个反逻辑的 bitwise const
class Text { ... char& operator[](std::size_t pos) const { return text[pos]; }private: char *text;};
. 在 clang 3.8 上编译失败,编译器已经修复这个反逻辑的问题。const 成员函数只能返回 const char& 类型的变量,这就保证了对象不能被修改。
当存在 const 成员函数和 non-const 成员函数的时候,可先实现 const 成员函数,non-const 成员函数通过调用 const 函数来实现,具体做法为先将对象转换为const类型(static_cast<const T&>()),调用const成员函数,再去除const属性(const_cast<T&>())
// constconst char& operator[](std::size_t pos) const { return text[pos]; }// non-constchar& operator[](std::size_t pos) { return const_cast(static_cast (*this)[pos]);}
. 这样做的好处是 const 成员函数保证了数据的不变,减少代码量,缺点是转换的性能缺失。
{/* body /*}
之前,使用 member initialization list
初始化对象是一种比较好的做法non-local static
编译器对不同编译单元的 non-local static 的编译顺序是随机的 class UnCopyable {public: UnCopyable() {} ~UnCopyable() {}private: UnCopyable(const UnCopyable&); UnCopyable& operator=(const UnCopyable&);};class Impl : private UnCopyable {};
delete
关键字class Impl {public: Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete;};
virtual
析构函数 override
来覆盖基类的 virtual
函数(析构函数可不用)final
关键字修饰类而禁止被继承virtual
函数 operator=
返回一个 reference to *thisoperator=
中处理“自我赋值” Resource Acquistion Is Initialization, RAII
auto_ptr
在标准中已经废除,用 unique_ptr
替代shared_ptr
互相引用的问题,可以用 weak_ptr
解决new
和delete
new
std::vector
或者 C++11 中的 std::array
代替new
对象置入指针指针 class
如同 typewarning: reference to stack memory associated with local variable 'a' returned [-Wreturn-stack-address]
private
(能够访问 private
的函数只有 friend
函数和成员函数) STL
保持一致性,这样 Widget 对象就可以正常的调用 swap 了class Widget {public: void swap(Widget& other) { using std::swap; swap(pImpl, other.pImpl); }private: WidgetImpl *pImpl;};namespace std { // std template <> void swap(Widget& a, Widget& b) { a.swap(b); }}
namespace WidgetFpp { // 这里不是添加到 std 中了 class WidgetImpl {}; class Widget {}; templatevoid swap(Widget & a, Widget & b) { a.swap(b); }}
pimpl
手法的,或类似的 std::swap
,并调用 swap 成员函数using std::swap
, 自动匹配合适的那个 swap 函数实现
7 class Window {8 public:9 virtual void OnResize() {10 a = 3;11 } 12 int a;13 }; 14 15 class SpWindow : public Window {16 public:17 void OnResize() override { // C++ 1118 // static_cast(*this).OnResize(); // 这里 a = 019 Window::OnResize(); // a = 320 } 21 };
dynamic_cast
copy-and-swap
inline
inline
和 template
通常被定义于头文件内。 是因为通常而言,inlining 和 template 是编译期的行为,编译器必须知道被调用函数的本体继承与面向对象设计
Item 33 - 确认 public
继承塑造出 is-a
关系
public
继承适用于 base class 对象上的每件事情也能够作用于 derived class 对象上virtual
函数也是这样,但是有运行时多态,但是普通函数当想继承的时候就出问题了using base::func;
后相同参数和返回还是会覆盖 base class 中的对象Item 34 - 区分接口继承和实现继承
Item 35 - 考虑 virtual
函数以及以外的选择
non-virtual interface, NVI
手法也是将真正需要的改变的部分分离出来放进一个函数内,而虚函数内先处理前置条件,再调用该函数7 class Game {8 public:9 int health() const {10 std::cout << "Game::health()\n";11 int ret = doHealth();12 std::cout << "Game:: ret " << ret << '\n';13 return ret;14 }15 private:16 virtual int doHealth() const {17 std::cout << "Game:: doHealth()\n";18 return 1; 19 } 20 }; 21 22 class LOL : public Game {23 private:24 virtual int doHealth() const {25 std::cout << "LOL doHealth()\n";26 return 2; 27 } 28 }; 29 30 int main() {31 LOL lol;32 lol.health();33 } // 可以使用到 base class 默认的析构函数,参考 Item39,用 private 作种实现方式。// LOL 未定义虚函数的时候,结果为// Game::health()// Game:: doHealth()// Game:: ret 1// 定义虚函数后// Game::health()// LOL doHealth()// Game:: ret 2
strategy
策略使用函数指针替换虚函数,这样每个对象都可以更灵活的有自己的特定处理函数,和运行时可以改变函数7 class GameCharacter;8 class HealthCalcFunc {9 public:10 virtual int calc(const GameCharacter&) const {11 return 3; 12 } 13 }; 14 15 HealthCalcFunc defaultHealthFunc;16 17 class GameCharacter {18 public:19 explicit GameCharacter(HealthCalcFunc* phcf = defaultHealthFunc) : pHealthCalc(phcf) {}20 int healthValue() const {21 return pHealthCalc->calc(*this);22 } 23 24 private:25 HealthCalcFunc* pHealthCalc;26 }; 27 28 int main() {29 GameCharacter gc;30 gc.healthValue();31 }
Item 36 - 绝对不重新定义继承而来的 non-virtual
函数
non-virtual
函数是静态绑定,父类指针只能表现出父类对象的行为, 换而言之,这个在编译期就确定好了Item 37 - 绝不重新定义继承而来的缺省参数值
virtual
函数,而它为动态绑定的struct B { virtual void mf(int i = 1) { std::cout << "B::mf " << i << "\n"; } // 不改变 virtual 函数的时候,可以使用 NVI 手法,实现一个外围函数,令子类调用它};struct D : public B { void mf() { std::cout << "D::mf\n"; }};int main() { D d; B *pb = &d; pb->mf(); // B::mf}
Item 38 - 通过复合 (composition)
塑造或根据某物实现出 has-a
has-a
,在实现域复合意味着 is-implemented-in-terms-ofItem 39 - 谨慎使用 private
继承
private
继承作为一种实现技术,意味着 is-implemented-in-terms-of, base class 为实现细节is-a
关系时,其中一个需要访问另一个的 protected
成员,或者需要 重新定义 其一或者多个vitual
函数,可以考虑private
继承Item 40 - 谨慎使用多重继承
模板与泛型编程
typename
的双重意义 template
参数时,typename
和 class
没有区别typename
标示嵌套从属类型名称(形如 C::const_iterator),但不得在 base class lists 或者 member initialization list 以他作为 base class 标示this
指针指向 base class 对象,具现base classstruct CompanyA { void sendClear(const std::string& msg) {}};struct CompanyB { void sendClear(const std::string& msg) {}};struct MsgInfo {};templateclass MsgSend {public: void send(const MsgInfo& info) { std::string msg = "foo"; Company c; c.sendClear(msg); }};template class LogMsg : public MsgSend {public: // using MsgSend ::send; // slove - way 2: tell complier, send() is defined in base class void sendMsg(const MsgInfo& info) { // send(info); // base: cant pass complier // this->send(info); // slove - way 1: could pass complier // MsgSend ::send(info); // slove - way 3: same as way 2 }};int main() { MsgInfo mi; LogMsg ma; ma.send(mi);}
template
member function template
成员函数模板可接受所有兼容类型参数,也就是泛化了构造函数friend
,感觉变复杂了traits classes
表现类型信息 一些新的知识点
转载地址:http://qeuwk.baihongyu.com/