1

Тема: Список ініціалізація для вектора працює неочікувано

Припустимо я маю такий клас

    class Tree
    {
    public:
        Tree() {}
        Tree(std::vector<Tree> children) : m_children(children) {}

        const std::vector<Tree>& GetChildren() { return m_children; }

    private:
        std::vector<Tree> m_children;
    };

Намагаюсь інстанціювати його так

    Tree t({ {} });

або так

    Tree t({ Tree() });

Та в результаті вектор дітлахів має розмір 0. Як це так?

А отак працює, отримую вектор з одним елементом

    vector<Tree> v{ {} };
ukrainian.stackexchange.com - це питання-відповіді з української мови

2 Востаннє редагувалося 0x9111A (15.01.2016 18:36:14)

Re: Список ініціалізація для вектора працює неочікувано

Думаю вам треба конструктор з initializer_list 
vector<Tree> v{ {} }; викликає саме такий конструктор (надіюсь, на жаль не маю змоги перевірити)

Maybe a = Just a | Nothing
Подякували: Yola, koala2

3 Востаннє редагувалося -=ЮрА=- (15.01.2016 18:41:50)

Re: Список ініціалізація для вектора працює неочікувано

що це за дизайн такий : класс який містить вектор таких же інстансів?Це помилка!Класс може тримати вектор вказівників на парентів, проте ніяк не вектор тих самих інстансів.

class Tree
 {
    private:
        std::vector<Tree> m_children;
 };

тут два варіанти
перший - код посту 1 наведено з наближенням до того що є у реалії, тоді потрібен реальний код
другий - помилка дизайна при проектуванні классу(я це вже зазаначив)

Подякували: Yola, koala, sensei3

4

Re: Список ініціалізація для вектора працює неочікувано

-=ЮрА=- написав:

що це за дизайн такий : класс який містить вектор таких же інстансів?Це помилка!

Дякую за зауваження пане! А чому?

ukrainian.stackexchange.com - це питання-відповіді з української мови

5

Re: Список ініціалізація для вектора працює неочікувано

Yola написав:
-=ЮрА=- написав:

що це за дизайн такий : класс який містить вектор таких же інстансів?Це помилка!

Дякую за зауваження пане! А чому?

Спробуйте відповісти на питання: який буде розмір такого класу, якщо там буде щось, крім вектора?

6 Востаннє редагувалося Yola (15.01.2016 22:43:30)

Re: Список ініціалізація для вектора працює неочікувано

koala написав:

Спробуйте відповісти на питання: який буде розмір такого класу, якщо там буде щось, крім вектора?

40

Прихований текст
де підступ?
ukrainian.stackexchange.com - це питання-відповіді з української мови

7

Re: Список ініціалізація для вектора працює неочікувано

Насправді, звісно, так можна - бо vector містить посилання на динамічний масив, де лежать справжні елементи. Але це дійсно поганий дизайн.

8

Re: Список ініціалізація для вектора працює неочікувано

koala написав:

Насправді, звісно, так можна - бо vector містить посилання на динамічний масив, де лежать справжні елементи. Але це дійсно поганий дизайн.

А вікіпедія каже, що там можна

struct Node
{
    int value; // певна інформативна частина
    Node * next; // вказівник (pointer) на наступну структуру-вузол у списку
};

Один, мабуть, розумний сайт подає оце

struct TreeNode {
              int item;         // The data in this node.
              TreeNode *left;   // Pointer to the left subtree.
              TreeNode *right;  // Pointer to the right subtree.
           }

А з вектором значить не можна? А якщо в мене дерево не бінарне, а я не знаю наперед скільки арне? *SCRATCH*

ukrainian.stackexchange.com - це питання-відповіді з української мови

9

Re: Список ініціалізація для вектора працює неочікувано

Дерева зазвичай - динамічні структури. Як ви частину дерева перенесете, коли вони жорстко прописані в масивах? Звісно, треба зберігати вказівники, типу vector<Tree*>. Але, ще раз, можна і vector<Tree>, бо vector містить в собі посилання.

10 Востаннє редагувалося -=ЮрА=- (16.01.2016 15:45:36)

Re: Список ініціалізація для вектора працює неочікувано

Дякую за зауваження пане! А чому?

- тому що це совго рода рекурсія, давайте по простому беремо інстанс що у ньому вектор, а у векторі що інші інстанси з вектором того самого...Вам не здається, що ви замість дерева пишите модель всесвіту чи фрактал, де йде ділення до бескінечності?Я вже не кажу про навігацію по цьому страхіттю.
По друге ви використовуєте вектор,

А з вектором значить не можна? А якщо в мене дерево не бінарне, а я не знаю наперед скільки арне?

який при ресайзі вимагає видалаення попередніх інстнсів, перезамовлення пам'яті а потім визову операторів копіювання(це якщо ви не хочете втратити попередні данні, тобто ви ще resize перепишете самі бо кожного раза видаляти та копіювати наприклад якийс дамп розміром кілька сотень мегабайтів м'яко кажучи безглуздо), ви напевне думаєте що меморі сама собою там плюсується - розачарую вас. Розберіть цей код, подивитесь скільки разів у векторі все перестворюється та видаляється http://codepad.org/H1hpVfhR

Прихований текст

#include <vector>
#include <iostream>
using namespace std;

class cItem{
public:
    char *str;    
    cItem(const char * str = 0) {
        if( str )
        strcpy(cItem::str = new char[1 + strlen(str)], str);
        else
        cItem::str = 0;
        cout<<"DFLT CONSTRUCT : "<<(cItem::str ? cItem::str : "null")<<endl;
    }
    cItem(const cItem &pCopy)
    {
        char * str = const_cast< char *>(pCopy.str);
        if( str )
            strcpy(cItem::str = new char[1 + strlen(str)], str);
        else
            cItem::str = 0;
        cout<<"COPY CONSTRUCT : "<<(cItem::str ? cItem::str : "null")<<endl;
    }
    ~cItem(){
        cout<<"DESTRUCT : "<<(cItem::str ? cItem::str : "null")<<endl;
        delete str;
    }
};

int main(){
    cItem itemArr[] = {
        cItem("test1"), 
        cItem("test2"), 
        cItem("test3")};
    vector< cItem > itemVec(itemArr, itemArr + 3);
    cout<<"INITIAL"<<endl;
    cout<<itemVec[0].str<<endl;
    cout<<itemVec[1].str<<endl;
    cout<<itemVec[2].str<<endl;
    cout<<"RESIZE"<<endl;
    itemVec.resize(5);
    cout<<itemVec[0].str<<endl;
    cout<<itemVec[1].str<<endl;
    cout<<itemVec[2].str<<endl;
    cout<<"EOP"<<endl;
    cin.get();
    return 0;
}

Output

DFLT CONSTRUCT : test1
COPY CONSTRUCT : test1
DESTRUCT : test1
DFLT CONSTRUCT : test2
COPY CONSTRUCT : test2
DESTRUCT : test2
DFLT CONSTRUCT : test3
COPY CONSTRUCT : test3
DESTRUCT : test3
COPY CONSTRUCT : test1
COPY CONSTRUCT : test2
COPY CONSTRUCT : test3
INITIAL
test1
test2
test3
RESIZE
DFLT CONSTRUCT : null
COPY CONSTRUCT : null
COPY CONSTRUCT : null
COPY CONSTRUCT : test1
COPY CONSTRUCT : test2
COPY CONSTRUCT : test3
COPY CONSTRUCT : null
COPY CONSTRUCT : null
DESTRUCT : test1
DESTRUCT : test2
DESTRUCT : test3
DESTRUCT : null
DESTRUCT : null
DESTRUCT : null
test1
test2
test3
EOP
DESTRUCT : test1
DESTRUCT : test2
DESTRUCT : test3
DESTRUCT : null
DESTRUCT : null
DESTRUCT : test3
DESTRUCT : test2
DESTRUCT : test1

У вас дизайн поганий тим що він рекурсивний, а не древовидний.
Якщо ви вирішили сторити дерево то застосовйте або двосполученний список( який не потребує видалення та копіювання попередньо накопленого) а краще використовуйте трисполученний список. Я показую двосполученний(там взагалі то список списків який імітє дерево), але якщо T зробити за поінтером то зможете у T хранити інші такі самі дерева. http://codepad.org/zMRCw2ga

Прихований текст

#include<list> 
#include<iostream>
using namespace std;

class cBase{
    public:
    virtual ~cBase(){}//робимо клас поліорфним
};

class cChild : public cBase{
    public:
    //тут що завгодно
    virtual void metod1(){};
};

template<class T>
class cListItem
{
public:
    T data;//під дата розумію якогось далекого cChild  з купою методів та полів
    cListItem<T> * prev;
    cListItem<T> * next;
    cListItem(){
        //прошу, робіть це відразу!
        prev = 0;
        next = 0;
    }
};

template<class T>
class cTList : public list<T>{
    public:
    //push, pop, set переопреділяємо як хочемо
};

template <class T>
class cItemList : public cTList < cListItem<T> >
{
public:
};

template<class T1, class T2>
class cTreeItem : public cListItem<T2>{
public:
    T1 tkey;//сюди ложіть ключа
    //T2 data;//сюди ложіть cItemList T2 = cItemList <якийсь інстанс>
};

template<class T1, class T2>
class cItemTree : public list<cTreeItem<T1, T2> >
{
public:
};


int main(){
    return 0;
}

PS:У любому випадку можете мене не слухате("наб'єте шишок", тоді повернитсь до цього посту перечитаєте, думаю зрозумієте, бо я сам не люблю коли нав'язують, як і що робити...)

Post's attachments

Untitled.png 20.8 kb, 55 downloads since 2016-01-16 

Подякували: Yola1

11

Re: Список ініціалізація для вектора працює неочікувано

-=ЮрА=- написав:

Вам не здається, що ви замість дерева пишите модель всесвіту чи фрактал, де йде ділення до бескінечності?

Та не здається, адже я наприкінці кожної гілки поставлю Tree() у якого немає дочірніх.

-=ЮрА=- написав:

Я вже не кажу про навігацію по цьому страхіттю.

Навігація надзвичайно просто, це дуже велика перевага цієї структури. Наприклад, щоб пройти шлях [1,7,3,1149], мені потрібно зробити так t.GetChidren()[1].GetChidren()[7].GetChidren()[3].GetChidren()[1149].

-=ЮрА=- написав:

По друге ви використовуєте вектор,

Так, і це дуже круто!

-=ЮрА=- написав:

який при ресайзі

а я не буду його ресайзити, мені потрібно дерево для представлення структури, яка наперед відома, наприклад, читається з файлу.

-=ЮрА=- написав:

а потім визову операторів копіювання

ну можна використати семантику переносу у своїх об'єктах, і ніяких копіювань якихось дампів

-=ЮрА=- написав:

У вас дизайн поганий тим що він рекурсивний, а не древовидний.

Не зрозумів, чим це

template<class T> class cListItem {
    cListItem<T> * prev;
};

відрізняється від цього

class Tree {
    std::vector<Tree> m_children;
};

кількістю елементів?  у вас один, а у мене довільна кількість?  це і відмінність рекурсивного дизайну від древовидного?


А зараз увага! рекурсія!

-=ЮрА=- написав:

У вас дизайн поганий тим що він рекурсивний ...

Дякую за зауваження пане! А чому?

ukrainian.stackexchange.com - це питання-відповіді з української мови

12 Востаннє редагувалося -=ЮрА=- (16.01.2016 16:43:52)

Re: Список ініціалізація для вектора працює неочікувано

Yola замість писати кучу рядків я процитую самого себе

У любому випадку можете мене не слухате("наб'єте шишок", тоді повернитсь до цього посту перечитаєте, думаю зрозумієте, бо я сам не люблю коли нав'язують, як і що робити...)

- колись ви мене зрозумієте, до того часу щоб щось вам прояснити мені доведеться витрачати купи часу на пояснення тієї чи іншої речі(вибачте вільний час у мене обмежений). Вам ваш концепт здається доречним, пишіть.
Хоча, взагалі то, спробую в останнє вас наштовхнути - якщо у вас на верхньому рівні 10 элементів і по якійсь надзвичайній причині ви захочете приэднати ще 11-й(ну ось захотіли 11 гілку), ви будете перекопіювати всі 10 гілок, ви важаєте це вірним рішенням?Далі, як ви вважаєте що швидше - бінарний пошук за ключем чи пошуковий прохід усього дерева? Дайте відповіді для себе, мені вони вже відомі.

13

Re: Список ініціалізація для вектора працює неочікувано

-=ЮрА=-, уявіть собі такий дизайн:

class Tree
{
  public:
  Tree(){}
  Tree(int branchCount):branchCount_(branchCount), branches( new Tree[branchCount] ) {}
  private:
  int branchCount_;
  Tree *branches_;
};

Такий дизайн має право на існування? От саме його і реалізував Yola. Проблема тут тільки в тому, що vector має надмірну функціональність для цього випадку, це треба якось позначити. Звісно, це не ООП (хоча б тому, що типи гілок одразу виставлені, що вбиває поліморфізм). Але C++ таке цілком допускає.

Подякували: Yola1

14 Востаннє редагувалося -=ЮрА=- (16.01.2016 17:13:24)

Re: Список ініціалізація для вектора працює неочікувано

koala, питання до вас, динамічно розширте/зменште верхній рівень(та взагалі любий рівень окрім останнього), якщо ви не напишите деструктор який приб'є branches_ отримаєте меморілік. Якщо будете намагатись перемістити поінтери, то з другого рівня вам прийдеться писати для кожного Tree ще хоча б swap(бо Tree ще має містити якийсь блок данних а не тільки масив з іншою гілкою). Там взагалі ще багато чого.

15 Востаннє редагувалося Yola (16.01.2016 16:53:11)

Re: Список ініціалізація для вектора працює неочікувано

koala написав:

Проблема тут тільки в тому, що vector має надмірну функціональність для цього випадку, це треба якось позначити.

Оце слушне зауваження.

-=ЮрА=- написав:

ну ось захотіли 11 гілку

та не захочу, я дизайнив клас для певного застосування. мені не потрібен ультрафункціональний клас дерева.

ukrainian.stackexchange.com - це питання-відповіді з української мови

16 Востаннє редагувалося -=ЮрА=- (16.01.2016 17:05:58)

Re: Список ініціалізація для вектора працює неочікувано

та не захочу, я дизайнив клас для певного застосування.

тоді пишіть, виникає питання навіщо ж тоді дерево - все складіть у рядок та юзайте
PS: Якщо колись захочете написати щось більш продвинуте, подивіться на Resize (ось цей момент newbranches_ i =  branches_i)

#include <iostream>
using namespace std;

class Tree
{
  public:
  ~Tree(){
    delete branches_;//це взагалі обов'язково!
  }
  Tree(int branchCount = 0){
        branchCount_ = branchCount;
        if( branchCount_ != 0 )
        branches_    = new Tree[branchCount_];
        else
        branches_    = 0;
  }
  void Resize(int newsize){
    //взагалі все виглядає десь так
    Tree *newbranches_ = 0;
    if( newsize )
    {
        newbranches_ = new Tree [newsize];
        for( int i = 0; i < newsize; i++ )
        {
            if( i < branchCount_ )
              newbranches_ [i] =  branches_[i];//ви гадаєте що це буде просто?
        }
    }
    delete branches_;
    branches_    = newbranches_ ;
    branchCount_ = newsize;
  }
private:
  int branchCount_;
  Tree *branches_;
};

http://codepad.org/MO5ZbV3J

17

Re: Список ініціалізація для вектора працює неочікувано

Та це ясно, але автор гарантує, що такого робити не треба. Ну... коли знадобиться, то хай сам викручується.
Спробую пояснити, що з цим кодом не так з іншого боку. Існує багато різних парадигм програмування: імперативне, функціональне, модульне, ООП і ще купа. Одна з переваг C++ - те, що вона не нав'язує парадигму. Хочете писати імперативно-процедурний код на кшталт C++ - будь ласка. Хочете ООП - прошу. Щось в ООП надто повільно працює - переписуйте фрагмент, як вам треба, без віртуальних функцій, будь ласка! Але водночас це спокушає користуватися цим багатством можливостей без потреби. Ну от який сенс в такому порізаному класі? А якщо завтра доведеться щось змінювати? В процедурному програмувані свої традиції, в ООП - свої, як писати гнучкий код. Ви ж пишете негнучку суміш стилів без потреби. Будь ласка, пишіть, але воно вам зробить боляче, повірте.