необходимость использовать указатели для абстрагирования класса вызывает трудности - PullRequest
3 голосов
/ 19 октября 2011

Мой первый вопрос здесь ...

Я хочу использовать абстрактный класс A, из которого я получил класс B (и классы B2, B3, ...),Я понял, что для их равномерной обработки я должен использовать указатели на базовый класс, поэтому здесь переменные типа A*.

В приведенном ниже примере я хочу заполнить aa, которыйвведите vector<A*> в функции f.Поскольку я могу использовать только ссылки, а переменные теряют свою область видимости в конце f, я больше не могу получить доступ к членам записей aa в main.

Что может быть решением дляэто проблема?

#include <vector>
#include <iostream>


class A {
    public:
        virtual int getVar(int, int) = 0;
};

class B : public A {
    public:
        B(std::vector<std::vector<int> > var) : var_(var) {};
        int getVar(int i, int j) { return var_[i][j]; }
    private:
        std::vector<std::vector<int> > var_;
};

void f(std::vector<A*>& aa) {
    std::vector<std::vector<int> > var(1, std::vector<int>(1));
    var[0][0] = 42;

    B b(var);

    aa.push_back(&b);
}

int main() {
    std::vector<A*> aa;

    f(aa);

    std::cout << aa[0]->getVar(0, 0) << std::endl;

    return 0;
}

Ответы [ 3 ]

7 голосов
/ 19 октября 2011
B b(var);
aa.push_back(&b);

Это адрес локальной переменной .Это является причиной проблемы, потому что локальная переменная уничтожается при возврате из функции, но aa все еще содержит указатель на несуществующий объект .Такой указатель называется висячий указатель , с помощью которого вызывается неопределенное поведение - что является одним из самых опасных и раздражающих аспектов C ++.

Использование new:

aa.push_back(new B(var));

Теперь все должно работать.

Важно: Не забудьте сделать ~A() виртуальным:

class A {
    public:
        virtual ~A() {} //MUST DO IT
        virtual int getVar(int, int) = 0;
};

Нужнов противном случае вы не сможете удалить объекты производного типа (четко определенным образом), используя указатели базового типа.

3 голосов
/ 19 октября 2011

Проблема, с которой вы столкнулись, является областью действия.

В C ++ существует две разные стратегии выделения:

  • автоматическое хранение: объект, объявленный в блоке ({ }), его время жизни заканчивается блоком
  • динамического хранения: объект, выделенный с new (или new[], если это массив)

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

Поэтому рекомендуется использовать идиому RAII, чтобы связать время жизни динамически размещенного объекта с временем жизни динамического объекта.автоматически распределяется один.В вашем случае используйте умные указатели (или контейнеры).

Используя C ++ 11:

int main() {
  std::vector< std::unique_ptr<A> > aa;
  foo(aa);

  std::cout << aa.front()->getVar(0,0) << "\n";
}

И соответственно обновите foo:

void foo(std::vector< std::unique_ptr<A> >& aa) {
  std::vector<std::vector<int> > var(1, std::vector<int>(1));
  var[0][0] = 42;

  aa.push_back(new B(var));
}
2 голосов
/ 19 октября 2011

Проблема в вашей функции f().Вы создаете автоматически распределенный объект, который перестает существовать после выхода из функции.Вы должны разместить его в куче, используя new.

B* b = new B(var);
aa.push_back(b);

Конечно, вы должны освободить его самостоятельно, когда вы больше его не используете.

int main() {
    ...


    for (std::vector<A*>::iterator it = aa.begin(); it != aa.end(); ++it)
    {
        delete *it;
    }
    return 0;
}
...