首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >作为多态类成员的智能指针

作为多态类成员的智能指针
EN

Stack Overflow用户
提问于 2011-11-09 14:21:14
回答 1查看 2.4K关注 0票数 4

我刚接触到智能指针,如果有人能给我提示一下作为类成员处理智能指针的方式是否正确,我将非常感激。更准确地说,我想要实现的解决方案是在类多态的上下文中实现的,并且应该是理想的异常安全的。

给定一个异形物体容器 (std::vector<shared_ptr<CBase> > my_vector),通常添加元素的方法是:my_vector.push_back( shared_ptr<CBase>(new CChild(1))),以便以后可以通过to:my_vector[0]->doSomething()调用特定派生类的成员函数。

  1. 我想要实现的是将堆栈对象添加到向量中,并且仍然能够进行多态。直觉地某事物比如:CChild<float> obj1(1); my_vector.push_back(obj1)。为了解决这个问题,我现在使用的是虚拟建设者成语CChild obj1(1); my_vector.push_back(obj1.clone());。 注意,在派生类的some中,我有创建对象的静态成员函数,例如:CChild<float> obj1 = CChild<float>::initType2(1);
  2. 由于需求问题,并且为了有一个干净的接口,我现在有了一个新的类CFoo<T>,它作为数据成员具有指向CBase<T>类的智能指针。 其思想是,除了包含其他新的私有成员之外,这个类封装/处理指向派生对象的智能指针,这样我就可以做一些事情了。像这样: CFoo<float> myfoo(CChild<float>::initType2(1)); my_vector.push_back(myfoo);。这意味着容器现在是vector<CFoo<T> >类型,而不是vector<shared_ptr<CBase> >类型。

正是在这种背景下,我想知道如何为类成员smart pointers实现构造函数?那么在复制交换成语之后operator =的实现呢?下面,我给出一些我的班级设计的试题:

代码语言:javascript
复制
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>对象填充容器,这些对象除了另一个整数类成员之外,还包含指向派生类的智能指针(注意,所有这些只是说明性的)。

代码语言:javascript
复制
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

代码语言:javascript
复制
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);
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-11-09 16:34:32

我真的不知道怎么处理这件事。我不理解你列出的接口要求的一半,所以考虑一下这是一个可能与你的问题完全无关的实验性答案。

我建议你告诉我我的方法到底遗漏了什么,我可以修正它。我现在省略模板,因为它们似乎与问题无关。

因此,最简单的开始使用的是一个智能指针容器:

代码语言:javascript
复制
#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

用法:

代码语言:javascript
复制
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();
}

如果您碰巧在某个地方有某个自动对象,可以插入一个副本:

代码语言:javascript
复制
DerivedD d = get_some_d();
v.push_back(BasePtr(new DerivedD(d)));

迭代:

代码语言:javascript
复制
for (BaseContainer::const_iterator it = v.begin(), end = v.end(); it != end; ++it)
{
  (*it)->foo();
}

更新:如果要在构造后初始化对象,可以执行如下操作:

代码语言:javascript
复制
{
  DerivedE * p = new DerivedE(x, y, z); 
  p->init(a, b, c);
  v.push_back(BasePtr(p));
}

或者,如果init函数是虚拟的,则更简单:

代码语言:javascript
复制
v.push_back(BasePtr(new DerivedE(x, y, z)));
v.back()->init(a, b, c);

第二次更新:这里是派生对象的样子:

代码语言:javascript
复制
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);

第三次更新:这个是不连接的,只是说明如何使用查找表来支持开关语句。假设我们有许多类似的函数(相同的签名),我们想要使用的是一些整数:

代码语言:javascript
复制
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支持:

代码语言:javascript
复制
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 },
// ...
};

基本上,您可以将静态代码转换为容器数据。这永远是件好事。这现在很容易扩展;您只需在查找映射中添加新的函数即可。不用再碰实际的do()代码了!

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8066372

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档