【问题标题】:What do *you* use C++ ABC constructors for?*你*使用 C++ ABC 构造函数做什么?
【发布时间】:2009-01-17 14:57:49
【问题描述】:

这里的人们在现场使用 C++ 抽象基类构造函数做什么?我说的是没有数据成员和非纯虚拟成员的纯接口类。

任何人都可以演示任何以有用的方式使用 ABC 构造函数的习语吗?或者仅仅是使用 ABC 实现接口的本质,它们保持为空、内联和受保护?

【问题讨论】:

    标签: c++ constructor interface-design abc


    【解决方案1】:

    任何人都可以演示任何以有用的方式使用 ABC 构造函数的习语吗?

    这是一个例子,虽然这是一个人为的、不常见的例子。

    您可以使用它来保存所有实例的列表:

    class IFoo
    {
    private:
      //static members to keep a list of all constructed instances
      typedef std::set<IFoo*> Set;
      static Set s_set;
    
    protected:
      //new instance being created
      IFoo()
      {
        s_set.insert(this);
      }
    
    public:
      //instance being destroyed
      virtual ~IFoo()
      {
        s_set.remove(this);
      }
    
      ... plus some other static method and/or property
          which accesses the set of all instances ...
    };
    

    或者仅仅是使用 ABC 实现接口的本质,它们保持为空、内联和受保护?

    更常见的是它们根本没有被声明!没有理由声明它们:

    • 空且内联 => 为什么要声明它?
    • Protected => ABC 可能已经有一些纯虚拟方法,因此除了作为子类之外已经无法实例化。

    【讨论】:

    • 你的例子基本上就是我的想法。我想到的另一个想法是利用构造顺序,以便能够安全地调用一些兄弟姐妹或来自较低层次的成员,但我真的想不出这样做的用途。
    【解决方案2】:

    假设所有派生类都有一些共同的行为。例如在某个外部注册表中注册自己,或检查某些东西的有效性。

    所有这些通用代码都可以放在基类的构造函数中,并且会从每个派生类的构造函数中隐式调用。

    【讨论】:

      【解决方案3】:

      如何抽象基类的构造函数用于任何事情?

      假设您有一个抽象基类 B 和一个派生类 D。当创建 D 类型的对象时,首先调用 B 的构造函数,但此时对象“仍然”属于 B 类型(参见 @987654321 @) -- 特别是,从 B 的构造函数主体调用任何虚函数将调用这些函数的 B 自己的实现。但是如果 B 是纯抽象类,那么这些虚函数都没有定义,所以程序会立即崩溃。

      我猜你打算让 B 的构造函数调用最派生类(例如 D 的)虚函数实现,对吗?这通常是个坏主意,因为 D 的对象尚未完全构造,因此从 D 的虚函数实现内部对 D 中的成员变量的任何访问都将访问未初始化的内存。

      【讨论】:

      • “我猜你打算让 B 的构造函数调用最派生类(例如 D 的)虚函数实现,对吧?”不,我知道你不能那样做,可能应该把它放在我的帖子里。
      • andrew:你的意思是 ctor 使用静态成员做事,比如 ChrisW 的例子?由于您明确表示“没有数据成员和非纯虚拟成员”,您的选择非常有限......
      【解决方案4】:

      记住:“资源获取是初始化”

      有时我们使用抽象基类作为某种锁定机制。比如在多线程环境中,多个线程需要共享一个资源,那么线程可以使用构造函数来获取资源,使用析构函数来释放资源

      void PlayWithPaintBallGun(Target &target)
      {
          PaintBallGun paintBallGun;    // constructor waits until the gun is free,
                                        // then picks it up.
      
          paintBallGun.Aim(target);     // Shoot something
          paintBallGun.Fire();          //
      
                                        // Clever! The destructor is automatically
                                        // called when it goes out of scope. So we
                                        // can't forget to put the gun down.
      }
      

      雨果

      【讨论】:

      • 这并不能真正回答我的问题。 RAII 类本身不是 ABC,尽管它们可以使用它们。
      【解决方案5】:

      我想不出很多有用的例子。没有数据成员的类没有状态,因此无法初始化任何东西。不过,您可以让构造函数/析构函数为您记录日志。例如,要记录所有访问者对象的创建/销毁:

      class Visitor {
      public:
          Visitor() {
              std::cout << "Visitor@" << this << " created" 
                        << std::endl;
          }
      
          virtual ~Visitor() {
              std::cout << "Visitor@" << this << " destroyed" 
                        << std::endl;
          }
      
          virtual void visitA(A*) = 0;
          virtual void visitB(B*) = 0;
          // ...
      };
      

      【讨论】:

        【解决方案6】:

        通常它只是将成员初始化为合理的值。

        【讨论】:

        • 有时 ABC 确实有一个可以初始化的变量。
        猜你喜欢
        • 1970-01-01
        • 2014-06-02
        • 1970-01-01
        • 2013-02-14
        • 1970-01-01
        • 2011-10-10
        • 1970-01-01
        • 2017-04-11
        相关资源
        最近更新 更多