новые, общие указатели и виртуальные функции - PullRequest
0 голосов
/ 25 ноября 2011

В моей программе я столкнулся с другой трудностью (я храню их все и задаю много проблем в пачках: P). У меня есть функтор - все эти функторы наследуются от DuplicateFn - у него есть виртуальный оператор (), и каждый дочерний элемент должен написать свою версию этого.

Теперь один из дочерних объектов - называемый слиянием - может не сработать, и в этом случае ему следует попробовать запасной метод. В настоящее время этот класс выглядит так:

class MergeFn : public DuplicateFn {
public:
    MergeFn() : FallBack(new SkipFn())
    {
    }
    MergeFn(GMProject const* Out, GMProject const* In, DuplicateFn* f) 
        : DuplicateFn(Out, In), FallBack(f)
    {
    }
    virtual void operator() (GMProject::pTree& tOut, const GMProject::pTree& tIn) const {
        if (!GMProject::isMergeable(tOut.GetName())) {
            (*FallBack)(tOut, tIn); //virtual table resolves this to the correct class
        } else {
        }
    }
private:
    std::shared_ptr<DuplicateFn> FallBack;
};

Я думаю, что проблема ясно видна уже здесь - в конструкторе не по умолчанию этот метод захватывает владение данным параметром. - Это не то, что я хочу: он должен скопировать этот параметр и сохранить его в собственности.
Теперь я попробовал , FallBack(new DuplicateFn(f)) Однако это тоже не сработает - поскольку теперь возникает ошибка компиляции, когда он пытается создать экземпляр объекта из класса с помощью чисто виртуальных методов.

Так как я могу это сделать? - Нужно ли указывать конкретный конструктор для каждого типа? - Просто чтобы скопировать это? Или я должен пройти через RTTI? Я надеюсь, что есть лучший подход, чем эти 2.

редактировать Чтобы показать, как mergeFn инициализируется (и используется):

std::unique_ptr<detail::DuplicateFn> foo;
foo.reset(new detail::MergeFn(this, &Other, DuplicateFns.at(HandleDuplicate)));

DuplicateFns - это карта, которая помогает преобразовывать пользовательские входные данные (строки) в указатели функций. - Или как сейчас, указатели на объекты подтипа из DuplicateFn (тип указателя - DuplicateFn *)

Затем используется как метод обратного вызова.

ProjectTree.combine_if(tree, &SimilarTreeValue, foo.get());

, который объединяет 2 дерева в одно - когда SimilarTreeValue возвращает true, запись считается дубликатом. Если запись является листом, то вызывается третий параметр - функтор, о котором мы говорим:).

Ответы [ 3 ]

1 голос
/ 25 ноября 2011

Самое простое решение (на мой взгляд) - изменить сигнатуру конструктора так, чтобы он уже требовал общего указателя:

MergeFn(GMProject const * Out,
        GMProject const * In,
        std::shared_ptr<DuplicateFn> f) /* ... */

Второй вариант - наделить всю иерархию классов функциями clone():

struct Base
{
  virtual Base * clone() const { return new Base(*this); }
};

struct Der1 : Base
{
  virtual Der1 * clone() const { return new Der1(*this); }
};

Затем вы можете инициализировать FallBack(f->clone()).

Лично я бы пошел с первой версией, и я бы также проверил, невозможно ли заменить общуюуказатель с помощью уникального указателя.

0 голосов
/ 25 ноября 2011

new DuplicateFn(f)

, который только нарезает функтор f.

Вам нужна функция клона:

#include <typeinfo>
#include <cassert>

template <class T>
// runtime checked clone function
// T::do_clone() must be accessible to checked_clone
T *checked_clone (const T* that) {
    T *p = that->do_clone();
    assert (typeid (*p) == typeid (*that));
    return p;
}

// clone for an abstract class
#define IMPLEMENT_CLONE_ABSTRACT(Class) \
    friend Class *checked_clone<Class> (const Class* that); \
 \
public: \
    Class *clone_naked() const { \
        return checked_clone (this); \
    } \
    unique_ptr<Class> clone_unique() const { \
        return unique_ptr<Class> (checked_clone (this)); \
    } \     \
private: \
    virtual Class *do_clone() const = 0; \
/* end of IMPLEMENT_CLONE_ABSTRACT */

class Base {
    IMPLEMENT_CLONE_ABSTRACT(Base)
};

// clone for a concrete class
#define IMPLEMENT_CLONE_CONCRETE(Class) \
    friend Class *checked_clone<Class> (const Class* that); \
 \
public: \
    Class *clone_naked() const { \
        return checked_clone (this); \
    } \
    unique_ptr<Class> clone_unique() const { \
        return unique_ptr<Class> (checked_clone (this)); \
    } \     \
 \
private: \
    virtual Class *do_clone() const {  \
        return new Class (*this); \
    } \
/* end of IMPLEMENT_CLONE_CONCRETE */

class Derived : public Base {
    IMPLEMENT_CLONE_CONCRETE(Derived)
};
0 голосов
/ 25 ноября 2011

Вы можете использовать конструктор shared_ptr, который принимает пользовательский деструктор и передает ему пустую функцию.Так например.

void dontDelete(DuplicateFn *pFn ) {
  // Do nothing!
  }

class MergeFn : public DuplicateFn { 
public: 
    MergeFn() : FallBack(new SkipFn()) 
    { 
    }

    MergeFn(GMProject const* Out, GMProject const* In, DuplicateFn* f)  
        : DuplicateFn(Out, In), FallBack(f, dontDelete) 
    { 
    }

    virtual void operator() (GMProject::pTree& tOut, const GMProject::pTree& tIn) const { 
        if (!GMProject::isMergeable(tOut.GetName())) { 
            (*FallBack)(tOut, tIn); //virtual table resolves this to the correct class 
        } else { 
        } 
    }

private: 
    std::shared_ptr<DuplicateFn> FallBack; 
}; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...