Наследуют ли шаблонные классы членов переданных им классов? (В частности, std :: vector) - PullRequest
3 голосов
/ 16 апреля 2009

У меня вопрос по поводу векторов:

Если у меня есть std :: vector , этот вектор "наследует" функции-члены MyClass или нет? Если нет, то будет ли лучший способ обрабатывать индивидуально членов MyClass внутри цикла? Должен ли я создавать новый объект класса на каждой итерации и назначать ему текущий векторный итератор?

class MyClass
{
    public:
        void setMembers(std::string &string, int &num)
        {
            mystring = string;
            number = num;
        }
        string printString() { return mystring; }
        int printNumber() { return number; }

    private:
        std::string mystring;
        int number;
};

    MyClass test;
    std::vector<MyClass> vector;

    std::string string = "Test 1";
    int a = 3;

    test.setMembers(string,a);
    vector.push_back(test);

    for(unsigned int i = 0; i<vector.size(); i++) {
        cout << "Vector #" <<  << endl;
        // does vector inherit member functions here?
        cout << "mystring is: " << vector.printString()<< endl;
        cout << "number is  : " << vector.printNumber() << endl;
    }

Заранее большое спасибо за помощь.

Ответы [ 10 ]

4 голосов
/ 16 апреля 2009

Нет, экземпляр std :: vector не наследует ваши переменные-члены. Однако у объектов в векторе есть те члены, к которым вы можете получить доступ через operator [].

for (size_t i = 0; i < vector.size(); i++) {
    cout << "Vector #" << i << endl;
    cout << "mystring is: " << vector[i].printString() << endl;
    cout << "number is  : " << vector[i].printNumber() << endl;
}

Обратите внимание, что теперь мы говорим vector [i], который возвращает значение типа MyClass, которое имеет функции-члены printString () и printNumber ().

Вам следует перечитать главу о наследовании.

4 голосов
/ 16 апреля 2009

На первую часть вашего вопроса уже ответили другие. Никакой формы наследования не происходит. Вектор ведет себя как вектор, и ничего больше.

Есть два способа манипулировать массивами. Первый (очевидный) - через цикл for, как вы сказали:

for(size_t i = 0; i<vector.size(); i++) { // technically, you should use size_t here, since that is the type returned by vector.size()
    cout << "Element #" <<  << endl; // We're iterating through the elements contained in the vector, so printing "Vector #" doesn't make sense. There is only one vector
    cout << "mystring is: " << vector[i].printString()<< endl; // [i] to access the i'th element contained in the vector
    cout << "number is  : " << vector[i].printNumber() << endl;
}

Другой подход заключается в использовании алгоритмов, определенных в стандартной библиотеке. Как введение в те, я собираюсь разделить это на несколько шагов. Во-первых, каждый контейнер также определяет тип итератора. Итераторы концептуально похожи на указатели, указывающие на местоположение в контейнере. Поэтому вместо vector[i].printString() вы можете вызывать printString () для элемента, на который указывает любой данный итератор. (при условии, что итератор с именем iter, синтаксис будет iter->printString())

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

Итак, сначала давайте воспользуемся циклом, чтобы снова запустить контейнер, но на этот раз с помощью итераторов:

forstd::vector<MyClass> current = vector.begin(); current != vector.end(); ++current) {
    cout << "mystring is: " << current->printString() << endl;
    cout << "number is  : " << current->printNumber() << endl;
}

Пока не очень большое улучшение, хотя оно устраняет индексную переменную i, которая часто не требуется, за исключением счетчика циклов. Функции begin / end) возвращают итератор, указывающий на первый элемент в контейнере, а другой указывает один после конца итератора. Поэтому, когда мы перемещаем первый итератор вперед, мы знаем, что достигли конца, когда он равен конечному итератору. Таким образом, два итератора могут представлять любой диапазон элементов.

Теперь, когда у нас есть итераторы, мы можем использовать множество других приемов. Стандартная библиотека C ++ поставляется с рядом алгоритмов для обработки последовательностей элементов. Они расположены в заголовке <algorithm>.

Самый простой способ начать работу - это std::for_each, который почти заменяет цикл for. Это просто функция, которая принимает два итератора, определяя диапазон элементов, которые она должна обрабатывать, и действие, которое она должна выполнять над каждым из них. Итак, чтобы назвать это, нам нужно определить такое действие:

void Print(const MyClass& obj) {
    cout << "mystring is: " << obj.printString() << endl;
    cout << "number is  : " << obj.printNumber() << endl;
}

Вот и все. Функция, которая принимает тип элемента в качестве параметра и выполняет все, что нужно. Теперь мы можем позвонить for_each:

std::for_each(vector.begin(), vector.end(), Print);

Если вам нужно делать это часто, это экономит много печатания. Функция Print должна быть определена только один раз, и тогда каждый цикл for может быть заменен таким однострочником.

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

std::for_each(vector.begin() + 5, vector.end(), Print);

или возьмите только первые три элемента:

std::for_each(vector.begin(), vector.begin()+3, Print);

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

std::copy(vector.begin(), vector.end(), dest.begin());

И dest также может быть любым типом итератора, он не обязательно должен быть векторным итератором только потому, что источником является. На самом деле мы могли бы даже напрямую скопировать в std :: cout, если вы хотите распечатать содержимое напрямую (к сожалению, поскольку MyClass не определяет operator <<, это приведет к ошибке.)

Чтобы обойти эту маленькую проблему с std :: cout, мы могли бы использовать std::transform, который применяет некоторые преобразования к каждому объекту, а затем помещает результат в выходную последовательность. Поскольку мы не можем напрямую распечатать объект MyClass, мы могли бы просто преобразовать его в строку, которую можно распечатать:

std::string ToString(const MyClass& obj) {
  return std::string("mystring is: " + obj.printString() + "\nnumber is  :" << obj.printNumber() + "\n";
}

Опять довольно простой код. Мы просто создаем функцию, которая принимает объект MyClass и создает строку с желаемым выводом. Итак, давайте скопируем это прямо в std :: cout:

std::transform(vector.begin(), vector.end(), std::ostream_iterator(std::cout), ToString);

std::ostream_iterator создает специальный итератор потока вывода из std::cout, чтобы позволить ему функционировать как итератор. И снова, реальный код «делай это на всем в векторе» стал одной строкой. Фактическое действие, которое нужно выполнить, определяется один раз, в другом месте, поэтому не нужно загромождать код.

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

Я не буду винить вас, если вы предпочитаете пока использовать циклы for, поскольку их немного легче прогуливать. Я просто хотел показать вам, что они не являются «окончательным» ответом в C ++.

3 голосов
/ 16 апреля 2009

Рассмотрим это с точки зрения «Есть / имеет» Вектор не является "MyClass", у него есть "MyClass" (на самом деле он имеет 0 или более MyClass), но важно то, что это не MyClass, он просто хранит их

Каждый объект, который вы помещаете в вектор, остается там, вы можете ссылаться на каждый объект по позиции (как если бы это был массив объектов MyClass)

for(unsigned int i = 0; i<vector.size(); i++) {
    cout << "Vector #" <<  << endl;
    // does vector inherit member functions here?
    cout << "mystring is: " << vector[i].printString()<< endl;
    cout << "number is  : " << vector[i].printNumber() << endl;
}

однако принято использовать итераторы, которые действуют как «похожие» указатели на хранимые объекты, например,

for(std::vector<MyClass>::iterator i = vector.begin(); i != vector.end(); ++i) {
    cout << "Vector #" <<  << endl;
    // does vector inherit member functions here?
    cout << "mystring is: " << i->printString()<< endl;
    cout << "number is  : " << i->printNumber() << endl;
}

Надеюсь, это поможет прояснить ситуацию.

3 голосов
/ 16 апреля 2009

Вектор ничего не наследует от своего класса, но члены являются членами класса.

Предположим, у вас есть std::vector<MyClass> v;, и на самом деле есть несколько участников (похоже, вы в порядке .push_back ()).

Теперь вы можете вызывать функции MyClass из чего-то вроде:

for (int i = 0; i < v.length(); ++v)
   v[i].printString();

или

for (std::vector<MyClass>::const_iterator i = v.begin(); i != v.end(); ++i)
   i->PrintString();
2 голосов
/ 16 апреля 2009

Нет, вектор не "наследует" членов класса. Если вы хотите что-то сделать для каждого элемента вектора, используйте итератор:

for(vector<MyClass>::iterator i=vector.begin();i!=vector.end();i++) {
    cout << "mystring is: " << i->printString() << endl;
    cout << "number is  : " << i->printNumber() << endl;
}
2 голосов
/ 16 апреля 2009

Нет. Vector - это контейнер вокруг объектов вашего класса. Вам нужно проиндексировать свой вектор с помощью функций-операторов operator [] или at (), чтобы получить доступ к объектам вашего класса. Затем вы можете вызывать эти функции на ваших объектах.

* 1003 Е.Г. *

v [I] .printString ();

или

v.at (я) .pringString ();

1 голос
/ 16 апреля 2009

Нет, std::vector<MyClass> не наследует членов MyClass, но вы можете использовать средства STL и boost для выполнения операций над вектором без явного кодирования итерации для каждой необходимой операции.
Вот пример кода:

#include <vector>
#include <algorithm>
#include <boost/bind.hpp>

struct Bla
{
    Bla(int i = 0) : m_i(i) {}

    void print() { printf("%d ", m_i); }
    void printAdd(int a) { printf("%d ", m_i +  a); }

    Bla add(int a) { return Bla(m_i + a); }
    int geti() { return m_i; }

    int m_i;
};

void printInt(int i)
{
    printf("%d ", i);
}

int main(int argc, char *argv[])
{
    std::vector<Bla> bla;
    bla.push_back(Bla(1));
    bla.push_back(Bla(2));

    // print the elements in the vector
    std::for_each(bla.begin(), bla.end(), boost::mem_fn(&Bla::print));
    printf("\n");
    // a complex operation on the vector requiring an additional argument for the call
    std::for_each(bla.begin(), bla.end(), boost::bind(&Bla::printAdd, _1, 10));
    printf("\n");

    // extract a single member from the vector into a second vector
    std::vector<int> result;
    result.resize(bla.size());
    std::transform(bla.begin(), bla.end(), result.begin(), boost::bind(&Bla::geti, _1));
    // print the result
    std::for_each(result.begin(), result.end(), &printInt);
    printf("\n");

    // transform the vector into a different vector using a complex function that requires an argument.
    std::vector<Bla> result2;
    result2.resize(bla.size());
    std::transform(bla.begin(), bla.end(), result2.begin(), boost::bind(&Bla::add, _1, 10));
    std::for_each(result2.begin(), result2.end(), boost::mem_fn(&Bla::print));
    printf("\n");

    return 0;
}
1 голос
/ 16 апреля 2009

Нет. Вектор похож на массив; это набор того, что он содержит, а не подкласс того, что он содержит.

1 голос
/ 16 апреля 2009

Нет, это не так. Вам необходимо получить доступ к каждому члену коллекции:

cout << "Vector #" <<  << endl;
for( unsigned int i = 0; i <vector.size(); i++) {
    cout << "mystring at " << i << " is "  << vector[i].printString()<< endl;
    cout << "number at " << i << " is " << vector[i].printNumber() << endl;
}
0 голосов
/ 16 апреля 2009

Спасибо за все ответы! Это оказалось проще, чем я думал, и теперь это имеет большой смысл.

Мне не следовало использовать слово «унаследовано», поскольку оно означает совершенно другое.

В любом случае, все ваши ответы очень помогли. Большое спасибо!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...