2016-07-25 11 views
0

У меня есть простой класс контейнера, который указывает на абстрактный класс, и у меня есть функции для получения/установки указателя в классе контейнера. Более конкретно, класс выглядит следующим образом:C++ Добавить производный объект абстрактного класса в другой класс без оборванных указателей?

class Container 
{ 
    Abstract* thing; 
public: 
    void set(Abstract &obj) 
    { 
     thing = &obj; //danger of dangling pointer 
    } 

    Abstract* get() 
    { 
     return thing; 
    } 
}; 

Abstract является абстрактным классом. Как уже видно, существует опасность свисающего указателя. Я знаю, что могу сделать копию объекта (новый), а затем указать на него. Но я не могу создать экземпляр абстрактного класса. Какие существуют решения для этого?


Ниже приводятся лишь дополнительная информация:

Определения классов

class Abstract 
{ 
public: 
    virtual void something() = 0; 
}; 

class Base : public Abstract 
{ 
    int a; 
public: 
    Base() {} 
    Base(int a) : a(a){} 
    virtual void something() 
    { 
     cout << "Base" << endl; 
    } 
}; 

class Derived : public Base 
{ 
    int b; 
public: 
    Derived() {} 
    Derived(int a, int b) : Base(a), b(b){} 
    virtual void something() 
    { 
     cout << "Derived" << endl; 
    } 
}; 

Простые тесты

void setBase(Container &toSet) 
{ 
    Base base(15); 
    toSet.set(base); 
} 

void setDerived(Container &toSet) 
{ 
    Derived derived(10, 30); 
    toSet.set(derived); 
} 

int main() 
{ 
    Container co; 

    Base base(15); 
    Derived derived(10, 30); 

    Base *basePtr; 
    Derived *derivedPtr; 

    //This is fine 
    co.set(base); 
    basePtr = static_cast<Base *>(co.get()); 
    basePtr->something(); 

    //This is fine 
    co.set(derived); 
    derivedPtr = static_cast<Derived *>(co.get()); 
    derivedPtr->something(); 

    //Reset 
    basePtr = nullptr; 
    derivedPtr = nullptr; 

    //Dangling pointer! 
    setBase(co); 
    basePtr = static_cast<Base *>(co.get()); 
    basePtr->something(); 

    //Dangling pointer! 
    setDerived(co); 
    derivedPtr = static_cast<Derived *>(co.get()); 
    derivedPtr->something(); 

    return 0; 
} 
+0

Уход за разделом причины downvote? – silentwf

+2

Возможный дубликат [Что такое умный указатель и когда его использовать?] (Http://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use -one) – LogicStuff

+0

@silentwf Приоритетные причины разделяются во всплывающей подсказке, когда вы наводите указатель мыши на указатель мыши. –

ответ

6

Что вам нужно сделать, это определить ваши мне mory собственности конкретно.

Container::set принимает экземпляр Abstract по ссылке, которая, как правило, не означает передачу права собственности:

void set(Abstract &obj){...} // Caller retains ownership of obj, but now we have a weak reference to it 

Тогда бремя делеции не на вас.

Container::get возвращает указатель который подразумевает владение, указывая, что кто-то называет set не аннулирует переданный объект.

Abstract* get(){...} 

Это может быть проблематично, как вы уже заявили.

У вас есть несколько вариантов

  • закодировать эти владения памятью семантики в Container с надлежащей документацией (Code by contract)
  • Использование смарт-указатель, как std::shared_ptr

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

void set(std::shared_ptr<Abstract> obj){...} 
// now Container participates in the lifetime of obj, 
// and it's harder to nullify the underlying object 
// (you'd have to be intentionally misbehaving) 
+0

Потому что я ненавижу глупость, как 'delete myContainer.get();' могу я поговорить с вами в 'Abstract & get() {...}'? – user4581301

+0

@ user4581301: Зачем вы удаляете 'myContainer.get()'? Он удалит себя, а вызов 'delete' вручную приведет к ошибке сегментации. – AndyG

+0

Yup. Это небрежно, но вы можете это сделать. Проблема, скорее всего, не будет с вопиющей и немедленной ошибкой, но неправильно обработать то, что выглядит как необработанный указатель несколько десятков строк кода позже. Эта ссылка усиливает сложность случайного вкручивания в категорию преднамеренно неправильной работы. – user4581301

1

Если вы обеспокоены объект высвобождена в другом месте приводит к оборванному указателю, вы могли бы использовать увеличить смарт-указатели.

Boost smart pointers предоставит вам услугу по ведению бухгалтерского учета и поможет избежать такого случая.

Некоторая информация может быть найдена здесь: smart pointers (boost) explained

1

Это то, что std::unique_ptr для:

class Container 
{ 
    std::unique_ptr<Abstract> thing; 
public: 
    void set(std::unique_ptr<Abstract> obj) 
    { 
     thing = obj; 
    } 

    Abstract* get() 
    { 
     return thing.get(); 
    } 
}; 

Теперь Abstract объект «принадлежит» Container и будет очищен автоматически, когда Conatiner разрушается.

Если вы хотите указатель, который может жить дольше или может использоваться совместно между контейнерами mulitple, используйте вместо этого std::shared_ptr.

 Смежные вопросы

  • Нет связанных вопросов^_^