智能指针作为多态的类成员 [英] Smart pointers as class members for polymorphism

查看:133
本文介绍了智能指针作为多态的类成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新的智能指针,我真的很感激,如果有人可以给我一个提示,我处理智能指针作为类成员的方式是否正确。
更确切地说,我想实现的解决方案是在类多态的上下文中,应该是理想的异常安全。

I'm new to smart pointers and I would be really grateful if somebody could give me a hint whether the way I'm handling smart pointers as class members is correct. More precisely, the solution that I would like to achieve is in the context of class polymorphism and should be ideally exception-safe.

给定一个异质对象容器 std ::向量< shared_ptr< CBase>> my_vector ),通常添加元素的方法是: my_vector.push_back(shared_ptr< CBase>(new CChild(1))) code>,以便稍后可以通过执行以下操作来调用特定派生类的成员函数: my_vector [0] - > doSomething()

Given a container of heterogeneuous objects (std::vector<shared_ptr<CBase> > my_vector), the usual way to add elements is: my_vector.push_back( shared_ptr<CBase>(new CChild(1))), so that later on, one can call the member function of the specific derived class by doing: my_vector[0]->doSomething().


  1. 我想实现的是将堆栈对象添加到向量中,并且仍然能够执行多态。直观地sth。 like: CChild< float> obj1(1); my_vector.push_back(obj1)。为了解决这个问题,我现在使用虚拟构造函数 CChild obj1(1);注意,在我的派生类的一些中,我' ve静态成员函数创建对象,例如: CChild< float> obj1 = CChild< float> :: initType2(1);

  1. What I would like to achieve is to add stack objects to the vector and still being able to do polymorphism. Intuitively sth. like: CChild<float> obj1(1); my_vector.push_back(obj1). To solve that, I'm using now the Virtual Constructor Idiom: CChild obj1(1); my_vector.push_back(obj1.clone());.
    Note that in some of my derived classes, I've static member functions that create objects, e.g: CChild<float> obj1 = CChild<float>::initType2(1);

由于需求问题,我现在有一个新类 CFoo< T> 具有作为数据成员的一个聪明的指针 CBase< T> class。
这个想法是除了包含其他新的私有成员
这个类封装/处理智能指针到派生对象,这样我被允许做sth。如:
CFoo< float> myfoo(CChild< float> :: initType2(1)); my_vector.push_back(myfoo); 。这意味着容器现在是向量< CFoo< T> > 而不是类型向量< shared_ptr< CBase> >

Because of requirement issues and also to have a clean interface, I've now a new class CFoo<T> that has as data member a smart pointer to the CBase<T> class.
The idea is that besides containing other new private members, this class encapsulates/handles the smart pointers to the derived objects, such that I'm allowed to do sth. like:
CFoo<float> myfoo(CChild<float>::initType2(1)); my_vector.push_back(myfoo);. This means that the container is now of type vector<CFoo<T> > instead of type vector<shared_ptr<CBase> >

正是在这种情况下,我想知道如何实施一个具有智能指针作为类成员的类的构造函数?在复制交换惯用法之后 operator = 的实现如何?下面,我给出一些对我的类设计的例子:

It's in this context, that I would like to know how to implement the constructors for a class with smart pointers as class members? What about the implementation of the operator = following the copy-swap idiom? Below, I give some ilustrations of my class design:

template < typename T >
class CBase{
    public:
        CBase(){};
        virtual ~CBase(){};
        ...
        virtual CBase<T> * clone() const = 0;
        virtual CBase<T> * create() const = 0;
};

template < typename T >
class CChild1 : public CBase{
    public:
        ...
        CChild1<T> * clone() const  { return new CChild1<T>(*this); }
        CChild1<T> * create() const { return new CChild1<T>(); }
        static CChild1 initType1(double, double);
        static CChild1 initType2(int);

};

template < typename T >
struct type{
    typedef std::tr1::shared_ptr<T> shared_ptr;
};

template < typename T >
class CFoo{

    public:

        CFoo();
        CFoo( const CBase<T> &, int = 0 );
        CFoo( const CFoo<T> & );
        void setBasePtr( const CBase<T> & );
        void swap( CFoo<T> & );
        CFoo<T> & operator = ( CFoo<T> );
        ...
        ~CFoo();

    private:

        typename type<CBase<T> >::shared_ptr m_ptrBase;
        int m_nParam;

};

template < typename T >
CFoo<T>::CFoo()
    :m_nParam(0)
// How shall I handle here the "m_ptrBase" class member? e.g point it to NULL?
{

}

template < typename T >
CFoo<T>::CFoo(const CBase<T> & refBase, int nParam)
    :m_ptrBase(refBase.clone()), // Is this initialization exception-safe?
    m_nParam(nParam)
{

}

template < typename T >
CFoo<T>::CFoo(const CFoo<T> & refFoo)
    :m_ptrBase(refFoo.m_ptrBase),
    m_nParam(refFoo.m_nParam)
{

}

template < typename T >
void CFoo<T>::setBasePtr( const CBase<T> & refBase ){
    // ??? I would like to do sth. like: m_ptrBase(refBase.clone())
}

template < typename T >
CFoo<T>::~CFoo(){
    // The memory is going to be freed by the smart pointer itself and therefore
    // the destructor is empty, right?
}

template < typename T >
void CFoo<T>::swap( CFoo<T> & refFoo ){
//does this here makes sense?
    using std::swap;

    swap(m_ptrBase, refFoo.m_ptrBase);
    swap(m_nParam, refFoo.m_nParam);

}

template < typename T >
CFoo<T> & CFoo<T>::operator = ( CFoo<T> copyFoo ){
    copyFoo.swap(*this);
    return (*this);
}

下面是一个我想直观实现的例子。首先,我用容纳了包含智能指针的派生类的 CFoo< float> 对象填充容器,除了另一整数类成员(注意所有这只是说明性的) p>

Below an example on what I would like to intuitively achieve. First, I fill the container with CFoo<float> objects that contain smart pointers to derived classes, besides another integer class member (Note that all this is only illustrative).

std::vector<CFoo<float> > my_bank;
for (int b=0; b < 3; b++){
   float x = b*sqrt(2);
   my_bank.push_back( new CFoo<float>( CChild1<float>::initType2(x), b) );
}

for (double s= 1.0; s<= 8.0; s *= 2.0){
    my_bank.push_back( new CFoo<float>( CChild2<float>::initType2(x), 0) );
 }

一旦容器被填充,我想做一些操作,调用到 virtual 函数,例如 doSomething 专用于每个派生类。

Once, the container is filled, I would like to do some operations, calling to virtual functions, e.g. doSomething that are specialized in each derived class.

for (int i=0; i < (int)my_bank.size(); i++){
    int b = my_bank[i].m_nParam;
    CBase<float>* myChild = my_bank[i].m_ptrBase;

    myChild->doSomething( param1, param2, param3, ..., b);
}


推荐答案

如何解决这个问题。我不明白您列出的一半接口要求,因此,请考虑这是一个实验性的答案,可能与您的问题无关。

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

用法:

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)));

要迭代:

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));
}

或者,如果 init function是virtual,更简单:

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

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

第二次更新:派生对象的外观如下: p>

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
};

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

第三次更新:此连接未连接,只是说明如何使用查找表支持switch语句。假设我们有很多类似的函数(相同的签名),我们想使用基于一些整数:

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;
    }
  }
};

我们可以在查找表中注册所有函数,而不是开关。这里我假设C ++ 11支持:

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 },
// ...
};

基本上,将静态 em>。这总是一个好事。这现在很容易扩展;你只需在查找映射中添加新函数。无需再次触摸实际的 do()代码!

这篇关于智能指针作为多态的类成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆