Является ли использование std :: vector класса в качестве атрибута одного и того же класса хорошим выбором для разработки? - PullRequest
3 голосов
/ 29 февраля 2012

Моя постановка проблемы следующая. Существует понятие «домен», которое состоит из множества «поддоменов». Теперь эти субдомены являются самостоятельными доменами. Ниже приведен основной метод, которым я могу заниматься. Я, вероятно, могу использовать auto_ptr или что-то еще, но давайте пока оставим это.

class Domain
{
private:
    Domain* subdomains;
}

Однако я смог скомпилировать и запустить следующую программу, которая, как мне кажется, делает то же самое и дает мне то, что я хочу.

#include <iostream>
#include <vector>
#include <string>

class Domain
{
private:
    std::string name_;
public:
    std::vector<Domain> subdomains;
    Domain(std::string name) : name_(name) {};
    std::string name() {return name_;}
    void addSubDomain(std::string subDomainName);
};

void Domain::addSubDomain(std::string subDomainName)
{
    subdomain.push_back(Domain(subDomainName));
}

int main()
{
    std::cout<<"Hello, World"<<std::endl;
    Domain domain("wow");
    domain.addSubDomain("wow-child");
    std::cout<<"Domain name is "<<domain.name()<<std::endl;
    std::cout<<"Subdomain name is "<<domain.subdomain[0].name()<<std::endl;
    return 0;
}

Вывод, который я получаю при запуске, это

$./main 
Hello, World
Domain name is wow
Subdomain name is wow-child

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

EDIT

Если это не решение, то есть ли другое решение, которое я могу использовать, которое не связано с управлением необработанными указателями?

Ответы [ 3 ]

6 голосов
/ 29 февраля 2012

Это неопределенное поведение, потому что в момент определения члена класс еще не полностью определен. В этот момент необходимо создать экземпляр std::vector<Domain> из шаблона std::vector<T> в класс std::vector<Domain> (чтобы определить его размер, среди прочего). Когда это происходит, Стандарт требует, чтобы класс был полностью определен.

2 голосов
/ 29 февраля 2012

Как правильно указывает Йоханнес, ваша программа не определена. Но ваша программа была бы совершенно правильной, если бы она сделала это:

class Domain {
    …
    std::vector<Domain*> subdomains;
    …
};

Конечно, вам придется управлять указателями. (Исключительная безопасность, RAII, правило трех и т. Д. И т. Д. И т. П.).

0 голосов
/ 29 февраля 2012

После прочтения других ответов и многих других вопросов из stackoverflow. Я хотел бы обобщить ответ своими словами. Хотя Йоханнес ответил на вопрос правильно, я хотел бы добавить еще кое-что. Итак, вот оно.

  1. Поведение кода, приведенного в вопросе, не определено в C ++ 03 для всех типов использования по причинам, указанным в ответе Йоханнеса. Однако в стандарте C ++ 11 требование было изменено. Поведение все еще не определено, если класс специально не разрешает это.

    C ++ 11 $ 17.6.4.8 Другие функции (...) 2. эффекты не определены в следующих случаях: (...) В частности - если неполный тип (3.9) используется в качестве аргумента шаблона, когда создание экземпляра компонента шаблона, если это специально не разрешено для этого компонента.

  2. Текущие примеры работают из-за специфической реализации std :: vector, которая не содержит никаких атрибутов типа T, а только типа T *. Следовательно, это случай удачной компиляции, которая может или не всегда может работать в зависимости от контейнеров.

  3. Решение проблемы состоит в том, чтобы в настоящее время использовать Boost :: Containers, поскольку они специально поддерживают неполные типы .

  4. Указатель std :: unique_ptr и std :: shared_ptr можно использовать в качестве замены для необработанной модели указателя для управления, если нужно, с небольшими уловами. Ответ на этот вопрос здесь . И подробное объяснение можно увидеть здесь . Итак, если кому-то нужен std :: container, он может использовать контейнер указателей, используя вышеуказанные указатели. Однако есть некоторые условия, которые должны быть выполнены. Эти условия описаны в приведенных выше ссылках.

В общем, лучший способ сейчас - это использовать boost :: Containers. Тем не менее, они вводят зависимость от повышения. Если вы хотите избежать зависимости, то вы можете использовать std :: unique_ptr и std :: shared_ptr. Тем не менее, существуют некоторые условия и ограничения относительно того, что можно сделать.

boost :: shared_ptr также позволяет использовать неполные типы. Но это тоже имеет некоторые ограничения.

...