Smart pointers as class members for polymorphism

主宰稳场 提交于 2019-12-05 16:44:41

I really don't know how to approach this. I don't understand half the interface requirements you've listed, so consider this an experimental answer that may not relate to your problem at all.

I suggest that you tell me what exactly is missing from my approach, and I can amend it. I'll omit templates for now, since they don't seem to be relevant to the problem.

So, without further ado, the simplest start uses a container of smart pointers:

#include <vector>
#include <memory>

struct Base
{
  virtual void f();
};

typedef std::shared_ptr<Base> BasePtr;
typedef std::vector<BasePtr> BaseContainer;

struct DerivedA : Base
{
  virtual void f();
  // ...
};

// further derived classes

Usage:

int main()
{
  BaseContainer v;
  v.push_back(BasePtr(new DerivedB));
  v.push_back(BasePtr(new DerivedC(true, 'a', Blue)));

  BasePtr x(new DerivedA);
  some_func(x);
  x->foo()
  v.push_back(x);

  v.front()->foo();
}

If you happen to have some automatic object somewhere, you can insert a copy:

DerivedD d = get_some_d();
v.push_back(BasePtr(new DerivedD(d)));

To iterate:

for (BaseContainer::const_iterator it = v.begin(), end = v.end(); it != end; ++it)
{
  (*it)->foo();
}

Update: If you want to initialize an object after construction, you can do something like this:

{
  DerivedE * p = new DerivedE(x, y, z); 
  p->init(a, b, c);
  v.push_back(BasePtr(p));
}

Or, if the init function is virtual, even simpler:

v.push_back(BasePtr(new DerivedE(x, y, z)));
v.back()->init(a, b, c);

2nd Update: Here's how a derived object might look like:

struct DerivedCar : Base
{
  enum EType { None = 0, Porsche, Dodge, Toyota };

  DerivedCar(EType t, bool a, unsigned int p)
  : Base(), type(t), automatic_transmission(a), price(p)
  {
    std::cout << "Congratulations, you know own a " << names[type] << "!\n"; }
  }

private:
  EType type;
  bool automatic_transmission;
  unsigned int price;

  static const std::unordered_map<EType, std::string> names; // fill it in elsewhere
};

Usage: Base * b = new DerivedCar(DerivedCar::Porsche, true, 2000);

3rd Update: This one is unconnected, just an illustration of how to use lookup tables in favour of switch statements. Suppose we have lots of similar functions (same signature) that we want to use based on some integer:

struct Foo
{
  void do_a();
  void do_b();
  // ...

  void do(int n)
  {
    switch (n) {
      case 2: do_a(); break;
      case 7: do_b(); break;
    }
  }
};

Instead of the switch, we can register all functions in a lookup table. Here I'm assuming C++11 support:

struct Foo
{
  // ...
  static const std::map<int, void(Foo::*)()> do_fns;

  void do(int n)
  {
    auto it = do_fns.find(n);
    if (it != do_fns.end()) { (this->**it)(); }
  }
};

const std::map<nt, void(Foo::*)()> Foo::do_fns {
  { 3, &Foo::do_a },
  { 7, &Foo::do_b },
// ...
};

Basically, you turn static code into container data. That's always a Good Thing. This is now easily scalable; you just add new functions to the lookup map as they come along. No need to touch the actual do() code again!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!