栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

More C++ Idioms C++ 惯常用法总结

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

More C++ Idioms C++ 惯常用法总结

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 & mutate(Pair & p)

{

  return reinterpret_cast &>(p);

}

  • 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 RefPtr;

    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 创建。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/931576.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号