Можно ли создать вектор указателей?

Просто интересно, из-за проблемы, с которой я сталкиваюсь, можно ли создать вектор указателей? И если да, то как? В частности, в отношении использования итераторов и .begin() с ним, то есть: как мне превратить этот вектор в вектор указателей:

class c
{
     void virtual func();
};

class sc:public c
{
     void func(){cout<<"using func";}
};

sc cobj;

vector<c>cvect
cvect.push_back(cobj);
vector<c>::iterator citer

for(citer=cvect.begin();citer<cvect.end();citer++)
{
     citer->func();
}

person Community    schedule 03.05.2009    source источник


Ответы (6)


vector <c> cvect не является вектором указателей. Это вектор объектов типа c. Вы хотите vector <c*> cvect. и вы, вероятно, хотите:

cvect.push_back( new c );

И затем, учитывая итератор, вы хотите что-то вроде:

(*it)->func();

Конечно, вполне вероятно, что вам вообще не нужен был вектор указателей...

person Community    schedule 03.05.2009
comment
func() - это виртуальная функция, поэтому я предполагаю, что здесь требуется вектор указателей. - person Naveen; 03.05.2009
comment
И если push_back() срабатывает, вы, вероятно, хотите избежать утечки нового c. - person bk1e; 03.05.2009

Конечно.

vector<c*> cvect;
cvect.push_back(new sc);
vector<c*>::iterator citer;
for(citer=cvect.begin(); citer != cvect.end(); citer++) {
  (*citer)->func();
}

Что нужно иметь в виду:

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

e.g.:

 for(...) { delete *i; }

Это можно упростить, используя вектор shared_ptrs (например, boost::shared_ptr). Не пытайтесь использовать для этого std::auto_ptr, это не сработает (даже не скомпилируется).

Еще одна вещь, о которой следует помнить, вам следует избегать использования < для сравнения итераторов в вашем цикле, когда это возможно, это будет работать только для итераторов, которые моделируют итератор с произвольным доступом, что означает, что вы не можете изменить свой код, чтобы использовать, например. std::list.

person Logan Capaldo    schedule 03.05.2009

Да, это возможно и фактически необходимо использовать указатели, если вы хотите, чтобы ваш вектор содержал объекты из всей иерархии классов, а не одного типа. (Отказ от использования указателей приведет к ужасной проблеме нарезки объектов — все объекты молча преобразуется в тип базового класса. Это не диагностируется компилятором и почти наверняка не то, что вам нужно.)

class c
{
     void virtual func();
};

class sc:public c
{
     void func(){cout<<"using func";}
};

sc cobj;

vector<c*> cvect;             // Note the type is "c*"
cvect.push_back(&cobj);       // Note the "&"
vector<c*>::iterator citer;

for(citer=cvect.begin();citer != cvect.end();citer++)   // Use "!=" not "<"
{
     (*citer)->func();
}

Обратите внимание, что с вектором указателей вам нужно самостоятельно управлять памятью, поэтому будьте очень осторожны — если вы будете использовать локальные объекты (как указано выше), они не должны выходить из области видимости до того, как контейнер делает. Если вы используете указатели на объекты, созданные с помощью new, вам потребуется delete их вручную, прежде чем контейнер будет уничтожен. В этом случае вам обязательно следует рассмотреть возможность использования интеллектуальных указателей, таких как smart_ptr, предоставленный Boost.

person j_random_hacker    schedule 03.05.2009

Да, конечно.

// TestCPP.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <vector>


using namespace std;

class c
{
public:
    void virtual func() = 0;
};

class sc:public c
{
public:
    void func(){cout<<"using func";}
};

int _tmain(int argc, _TCHAR* argv[])
{
    sc cobj;

    vector<c*> cvect;
    cvect.push_back(&cobj);
    vector<c*>::iterator citer;

    for(citer=cvect.begin();citer<cvect.end();citer++)
    {
        (*citer)->func();
    }

    return 0;
}

Обратите внимание на объявление vector<c*> cvect и использование cvect.push_back(&cobj).

Из предоставленного кода вы неправильно используете итератор. Чтобы получить доступ к элементу, на который указывает итератор, вы должны использовать *citer вместо одного citer.

person Gant    schedule 03.05.2009

Вы создали vector<c*> для вектора указателей. Затем используйте new, чтобы выделить память для объектов c и поместить их в вектор. Кроме того, не забывайте, что вы должны delete самостоятельно, а vector.clear() не освободит память, выделенную для объектов c. Вы должны хранить здесь c как вектор указателей, иначе вызов виртуальной функции работать не будет.

person Naveen    schedule 03.05.2009

Попробуйте контейнерную библиотеку указателей Boost. Он имеет несколько преимуществ перед обычным вектором указателей, например:

my_container.push_back( 0 );            // throws bad_ptr 
ptr_vector<X> pvec; 
std::vector<X*> vec;
( *vec.begin() )->foo(); // call X::foo(), a bit clumsy
pvec.begin()->foo();     // no indirection needed
person Community    schedule 04.05.2009