2013-02-19 3 views
0

У меня есть класс. Когда этот класс создается, я хочу, чтобы экземпляр был добавлен в список. Когда объект удален, я хочу, чтобы он был удален из списка.Автоматическое добавление и удаление объекта из списка

Так что я даю объекту общий указатель на себя. Затем у меня есть список слабых указателей на эти общие указатели. Когда объект создается, он создает общий указатель на себя, делает слабый указатель на него и помещает слабый указатель в список.

Когда объект уничтожен, общий указатель также. Всякий раз, когда я пытаюсь получить доступ к члену в списке, я гарантирую, что он не истек, и что его количество использования не равно 0. Несмотря на это, я все еще сбой, когда элемент списка уничтожен. Зачем? Могу ли я обойти это? Вот мой SSCCE:

#include <iostream> 
#include <memory> 
#include <vector> 

class test 
{ 
    private: 
     std::shared_ptr<test> self; 

    public: 
     int val; 
     test(int set); 

     test(test &copy) = delete; // making sure there weren't issues 
            // with a wrong instance being deleted 
}; 

std::vector<std::weak_ptr<test>> tests; 

test::test(int set): 
    val(set) 
{ 
    this->self = std::shared_ptr<test>(this); 

    tests.push_back(std::weak_ptr<test>(this->self)); 
} 

void printTests() 
{ 
    for (auto i = tests.begin(); i != tests.end(); i++) 
    { 
     if (i->use_count() == 0 || i->expired()) 
     { 
      tests.erase(i); 
      continue; 
     } 

     std::cout << i->lock()->val << std::endl; 
    } 

    std::cout << std::endl; 
} 

int main(int argc, char **argv) 
{ 
    { 
     test t(3); 

     std::cout << "First tests printing: " << std::endl; 

     printTests(); 
    } // SEGFAULTS HERE 

    std::cout << "Second tests printing: " << std::endl; 
    printTests(); 

    return 0; 
} 

Вывод этой программы выглядит следующим образом:

First tests printing: 
3 

Segmentation fault (core dumped) 
+2

я буду практически * гарантия * вы, что 'this-> само = станд :: shared_ptr (это),' не делать то, что вы думаете, это, тем более, что реальный объект, 'тест т (3) ', находится в * стеке *. – WhozCraig

+0

То, что это в стеке, означает, что тест уничтожается в конце его области, и это то, что я хочу.Как еще я буду делать то, что хочу? – Avi

+1

Как вы думаете, 'std :: shared_ptr <>' делает с указателем объекта, содержащимся внутри, когда разрушен общий ptr obj? – WhozCraig

ответ

3

Ваш вопрос с тем, как вы создаете указатель сам:

this->self = std::shared_ptr<test>(this); 

Когда shared_ptr создан с помощью этого конструктора, в соответствии с documentation ,

Когда T не является типом массива, создается shared_ptr, которому принадлежит указатель p. ... р должен быть указателем на объект, который был выделен с помощью C++ новое выражение или быть 0

Так что вопрос в том, что shared_ptr принимает право собственности на объект стека, поэтому, когда объект получает destructed (и shared_ptr вместе с ним), shared_ptr пытается сделать delete ваш объект, находящийся в стеке. Это неверно.

Для вашего прецедента, если вы ожидаете, что test s переедет в ваш vector, тогда вы можете просто сохранить this.

+0

Отлично. Итак, как мне сделать то, что мне нужно? Я не могу заменить то, что я укажу в деструкторе, потому что мне нужно получить этот объект. – Avi

+1

@Avi: Почему бы просто не удалить деструктор из списка? –

+0

По причинам, которые я сказал дважды сейчас: мне нужно получить тест, и я бы предпочел не переписывать этот код для деструктора каждого производного объекта. Это приведет к поражению цели того, что я пытаюсь сделать здесь, что скрывает список от кодеров производных объектов. – Avi

1

Эта линия беда:

tests.erase(i); 

Итератор, указывающий на стертое элемент недействителен, и вы больше не можете его увеличивать. К счастью, erase возвращает новый итератор вы можете использовать:

auto i = tests.begin(); 
while (i != tests.end()) 
{ 
    if (i->use_count() == 0 || i->expired()) 
    { 
     i = tests.erase(i); 
    } 
    else { 
     std::cout << i->lock()->val << std::endl; 
     ++i; 
    } 
} 
+0

Если бы это было так, это был бы доступ к итератору, который вызвал бы проблему, верно? – Avi

+0

Причина для downvote? Этот ответ правильный, даже если это не единственная проблема. –

2

Я думаю, что OP заинтересован в решении его первоначальной проблемы, даже если он использует другой метод, чем тот, который он пытался. Ниже приведен простой пример того, как добавить объект в глобальный список при его создании и удалить его при его удалении. Одна вещь, которую нужно помнить: вы должны вызывать AddList в каждом конструкторе, который вы добавляете в базовый класс. Я не знал, хотите ли вы, чтобы список был доступен вне класса или нет, поэтому я добавил функции getter, чтобы возвращать неконстантные итераторы в список.

class MyClass 
{ 
private: 
    static std::list<MyClass*> mylist; 
    std::list<MyClass*>::iterator mylink; 

    // disable copy constructor and assignment operator 
    MyClass(const MyClass& other); 
    MyClass& operator = (const MyClass& other); 

    void AddList() 
    { 
     mylink = mylist.insert(mylist.end(), this); 
    } 

    void RemoveList() 
    { 
     mylist.erase(mylink); 
    } 

public: 
    MyClass() 
    { 
     AddList(); 
    } 

    virtual ~MyClass() 
    { 
     RemoveList(); 
    } 

    static std::list<MyClass*>::iterator GetAllObjects_Begin() 
    { 
     return mylist.begin(); 
    } 

    static std::list<MyClass*>::iterator GetAllObjects_End() 
    { 
     return mylist.end(); 
    } 

    virtual std::string ToString() const 
    { 
     return "MyClass"; 
    } 
}; 

class Derived : public MyClass 
{ 
    virtual std::string ToString() const 
    { 
     return "Derived"; 
    } 
}; 

std::list<MyClass*> MyClass::mylist; 


int main() 
{ 
    std::vector<MyClass*> objects; 
    objects.push_back(new MyClass); 
    objects.push_back(new MyClass); 
    objects.push_back(new Derived); 
    objects.push_back(new MyClass); 

    for (std::list<MyClass*>::const_iterator it = MyClass::GetAllObjects_Begin(), end_it = MyClass::GetAllObjects_End(); it != end_it; ++it) 
    { 
     const MyClass& obj = **it; 
     std::cout << obj.ToString() << "\n"; 
    } 

    while (! objects.empty()) 
    { 
     delete objects.back(); 
     objects.pop_back(); 
    } 
} 
+0

Да, альтернативное решение было бы хорошо, но, к счастью, мне удалось реализовать его самостоятельно. Спасибо за ответ. – Avi

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