CSDN 的图片粘贴上传又不能用了,图标全部看不见了。彩色是总结成面试题的意思....
内容来源,还没总结完。。。。边学边用:
| More C++ Idioms - Wikibooks, open books for an open world |
总结
- Acyclic visitor 没搞明白
- address of 是为了解决重载 operator & 的问题。
- Attach by initializaiton,对于 event-loop 的程序以及接管 main 的程序(通常是 GUI 或者像 gtest 这种),有一些 main 之前的逻辑 。实现方案是用 static storage,如果有多个翻译单元的顺序要求,通过写 meyers singleton 然后写一个初始化函数依次调用他们。
| 如果我需要在程序执行之前初始化一系列的对象,怎么初始化? |
- 律师 Attorney-client,因为 friend 可以访问所有 private 成员,解决方案是加一个中间人--律师
| 如何控制 friend 类能够看到的成员?/如何对 friend 的视图做权限限制? |
代码示例:
| class Client { private: void A(int a); void C(double c); friend class Attorney; }; class Attorney { private: static void callA(Client & c, int a) { c.A(a); } friend class Bar; }; class Bar { // Bar now has access to only Client::A and Client::B through the Attorney. }; |
- Barton-Nackman trick,就是说,模板类写 friend 的 operator 比较,而不是写在 scope 外面。这样比较函数就不是模板重新另外决议的函数了,而是实例化之后的函数。一种 CRTP 模式 wrapper。
| operator 函数重载的时候用成员函数、friend 和在 namespace 中写有什么区别和优缺点? |
代码示例:
| template class EqualityComparable { public: friend bool operator==(const T & lft, const T & rgt) { return lft.equalTo(rgt); } friend bool operator!=(const T & lft, const T & rgt) { return !lft.equalTo(rgt); } }; class ValueType : private EqualityComparable public: bool equalTo(const ValueType & other) const; }; |
- Base-from-Member,如果需要在 base 构造之前使用 derived-class 的 member 的时候,一般的做法是通过 `nullptr` 和额外的一个 init 函数来实现。这里讲解了一个通过依赖于类的构造顺序,从而成功的把 has-a 变成了加一个 middleware 的 is-a。
| 基类需要用到派生类的成员,怎么把派生类的成员传过去?/如何实现先初始化类的成员再初始化基类? |
| (其实这个是一个设计问题,尽量不要这样设计吧!但是有时候确实希望实现,比如非侵入式的修改,然后你又想用 has-a + inheritance 来封装一个类,比如下面的这个 fdostream。boost utility 还专门做了一个模板类)。 |
代码示例:
| class fdoutbuf : public std::streambuf { public: explicit fdoutbuf(int fd); //... }; struct fdostream_pbase // A newly introduced class.{ fdoutbuf sbuffer; // The member moved 'up' the hierarchy. explicit fdostream_pbase(int fd) : sbuffer(fd) {} }; class fdostream : protected fdostream_pbase // This class will be initialized before the next one. , public std::ostream { public: explicit fdostream(int fd) : fdostream_pbase(fd), // Initialize the newly added base before std::ostream. std::ostream(&sbuffer) // Now safe to pass the pointer. {} //... }; |
- Boost mutant=> BiMap 节约美德,对于一个 pair,如何避免复制的情况下实现 first 访问 second,second 访问 first 呢?简单,只需要进行一个 adapter 模式和暴力 POD `reinterpret_cast` 和引用/指针就行了。`boost::bimap` 的好处在于,避免复制的时候可以正向和反向同时查询,比如 hostname <-> ip address。
| 如果我需要查询大量 key 和 value 的相互查询,怎么优化? |
| 这种实现完全是编译开销,因为解释 first 和 second 只是不同的汇编解释。 |
代码示例:
| template struct Reverse { typedef typename Pair::first_type second_type; typedef typename Pair::second_type first_type; second_type second; first_type first; }; template Reverse { return reinterpret_cast } |
- Calling Virtuals During Initialization,首先复习几个问题,vptr 的填充是在 base ctor 之后, member init 之前进行的 [class.base.init]。因此构造函数中禁止了动态查找和虚函数绑定,此时只会调用自己的虚函数。解决方案即 two phase initialization ,一般结合 factory pattern 来做,不然 RAII 就无了,还是手写各种 init。法二是用 CRTP 不用虚函数,然后加限定域查找,但是太丑了而且 GP constraint 难以维护。
| 你知道 vptr 是什么时候填充的吗?(先问: vtable 是什么时候初始化的? |
| 基类调用虚函数的时候调用的是谁的实现?如果在构造函数调用呢?如果在析构函数中调用呢?(这不是 UB) |
| 构造函数中可以对外暴露 this 指针吗?为什么不能。(废话) |
- Construction tracker,利用逗号表达式(注意C++20 中下标内逗号不适用)实现构造成员捕获异常的 track。具体来说,就是构造的时候通过 `(tracker = TAG, (args...))` 作为参数去构造成员数据类型,从而可以知道是谁抛出的。或者另外起多个 bool 变量。
| 构造的时候怎么知道构造哪个成员对象时抛出了异常? |
补充一个我从来没用过的东西先:
| C++ 中构造 try catch block |
| class A{ ... public: A() try: m1(), m2()...{} catch(...){} }; |
从而可以设计这样的代码:
| ... A( TrackerType tracker = NONE) try // A constructor try block. : b_((tracker = ONE, "hello")) // Can throw std::exception , c_((tracker = TWO, "world")) // Can throw std::exception { assert(tracker == TWO); // ... constructor body ... } ... |
- Copy-on-write,这个 chensuo 在 muduo 书里面写过,曾经的 gcc string 还用过。基本原理是用 const 访问的时候,就用原来的 shared_ptr, 如果需要用非 const 利用的时候,比如解引用或者调用了某些 setter ,利用 `shared_ptr` 的 `unique()` 接口判断(这个东西是不是安全的知乎还有一个问题讨论,SO 也有....)要不要复制一份。
| 有一个很大的对象可能需要多个线程用到,大部分是读,少部分需要修改,怎么优化? |
示例代码:
| template class CowPtr { public: typedef std::shared_ptr private: RefPtr m_sp; void detach() { T* tmp = m_sp.get(); if( !( tmp == 0 || m_sp.unique() ) ) { m_sp = RefPtr( new T( *tmp ) ); } } public: ... impl all copy/move stuffs... }; |
- EBO:不说了,太简单了,对于可能的空类(最常见的是 Functor,用他基本是为了用他的 `operator()`),通过继承实现组合。一般 wrap 一个中间层,比如 stl 的 vector、map 等非 pmr allocator 的就是这样做的。但是 EBO 了之后,不同类的地址就可以一样,标准也没有保证多继承的时候地址可以区分。
| 空类的大小是多少?如果要用到很多空类,怎么优化? |
- Erase-Remove:不知道这个为什么算一个 idiom... 大概是说,`std::remove` 只是 move,并不是 remove。因为他是通用的,他没有用 container 的引用参数,因此没办法修改 meta。所以一般要结合 `containter.erase` 来使用...
| 怎么从容器类里面删除元素?(std::remove 怎么实现的?)/这个根本不算一个能问的问题... |
已使用 OneNote 创建。



