По поводу vector<>
есть класс к примеру myClass объявленный примерно так
class myClass:private myBaseClass{
private:
typedef vector<Class>ListClass;
...
...
ListClass listclass;
}
Объекты данного класа помещаются в другом классе в map. Т.е.
class MyListClass{
typedef map<myClass>ListMyClass;
...
...
ListMyClass listmyclass;
}
Все функции необходимые для помещения извлечения и пр. реализованы и работают.
За исключением одного но - когда я считываю из мапа объекты класса - то listclass для всех объектов одинаков. Как это побороть? Мне необходимо что бы каждый объект myClass содержал уникальный вектор для каждого элемента. Может есть какие то идеи?
Ситуация следующая:
есть класс к примеру myClass объявленный примерно так
class myClass:private myBaseClass{
private:
typedef vector<Class>ListClass;
...
...
ListClass listclass;
}
Объекты данного класа помещаются в другом классе в map. Т.е.
class MyListClass{
typedef map<myClass>ListMyClass;
...
...
ListMyClass listmyclass;
}
Все функции необходимые для помещения извлечения и пр. реализованы и работают.
За исключением одного но - когда я считываю из мапа объекты класса - то listclass для всех объектов одинаков. Как это побороть? Мне необходимо что бы каждый объект myClass содержал уникальный вектор для каждого элемента. Может есть какие то идеи?
Ну он и должен быть уникален, он же не static.
Проверь, что у тебя классы соответствуют требованиям контейнеров, а именно:
- имеют конструктор поумолчанию,
- имеют конструктор копирования,
- иногда требуется еще и operator=
Проблема может быть еще и в том, как ты заполняешь vector и map.
Короче, надо смотреть подробнее.
И еще, не относящееся к этой проблеме, ты уверен, что тебе нужен vector, а не list или даже queue?
Ну он и должен быть уникален, он же не static.
Проверь, что у тебя классы соответствуют требованиям контейнеров, а именно:
- имеют конструктор поумолчанию,
- имеют конструктор копирования,
- иногда требуется еще и operator=
Проблема может быть еще и в том, как ты заполняешь vector и map.
Короче, надо смотреть подробнее.
И еще, не относящееся к этой проблеме, ты уверен, что тебе нужен vector, а не list или даже queue?
Да мне то нужен простой шаблон, в который помещаются классы, как раз вектора с головой хватает. Просто проблема загадочная...:x Запись из файла, запись в файл - все работает на ура - классы добавляются без проблем...а вот стал работать с отдельными элементами меп'а - и вдруг такая неприятность. Вектор заполнен так как надо, только все классы обращаются к одному и тому же.
А это не есть здорово.
Проблема была в том, что базовый класс был по началу совсем не базовым а конкретным, и с тех времен в нем был использован:
typedef vector<Class>ListClass;
...
ListClass listclass;
и вроде как происходило перекрывание пространства имен. Базовый класс создается один раз, поэтому и возникал единый массив для всех объектов. Может и не совсем верно, с точки зрения теоретической, но практически заработало...:)
и вроде как происходило перекрывание пространства имен.
Это как?
Базовый класс создается один раз, поэтому и возникал единый массив для всех объектов.
Класс создается? В C++ классы сами не создаются, создаются екземпляры классов
Да мне то нужен простой шаблон, в который помещаются классы,
Интересное заявление... Может все же нужен контейнер, а не любой шаблон. :D
как раз вектора с головой хватает.
Вектор - это контейнер, который каждый раз при довавлении/удалении элемента заново перераспределяет память для всех элементов. Поэтому он медленный и тяжеловесный. Если нет необходимости хранить элементы одним куском в памяти, то лучше использовать связанные списки (list, queue).
Это как?
Честно говоря - не знаю. Это мое предположение, не подтвержденное пока ничем. После удаления вектора из базового класса - все вроде заработало, потом опять начали возникать проблемы, в конце концов, от вектора в качестве поля класса я просто отказался. просмотр доков, как по Билдеру так и просто по С++ ничего не дал. Ни единого упоминания о чем либо подобном мне не встретилось. Правда в одном моменте проскочила рекомендация в случае использования шаблонов - контролировать копирование и создание объектов класса - но в связи с чем и какие проблемы могут возникнуть???? И возможно ли их обойти? Ни гу-гу.
Класс создается? В C++ классы сами не создаются, создаются екземпляры классов
Прошу прощения, именно это и имелось подразумевалось.
Вектор - это контейнер, который каждый раз при довавлении/удалении элемента заново перераспределяет память для всех элементов. Поэтому он медленный и тяжеловесный. Если нет необходимости хранить элементы одним куском в памяти, то лучше использовать связанные списки (list, queue).
Как раз вектор для данной задачи и наиболее подходил. Потому как, кроме однократной вставки элементов и получения элементов по индексу - ни каких операций более не проектировалось. И лист и дек для этих целей чересчур. Да и собственно проблему в конечном итоге они не решают.
Значит все было примерно так:
{
private:
typedef vector<Class> ListClass;
ListClass listclass;
};
class myClass :private myBaseClass
{
private:
typedef vector<Class> ListClass;
ListClass listclass;
};
class MyListClass
{
typedef map<myClass> ListMyClass;
ListMyClass listmyclass;
};
Т.о. в классе myClass существуют два вектора:
myClass::listclass;
и
myClass::myBaseClass::listclass;
Причем второй недоступен даже из самого класа, если в базовом он объявлен, как privat.
Если методы работы с вектором были расположены в базовом классе, то они и работать будут только с вектором расположенном в базовом классе. Методы расположенные в дочернем классе будут работать в данном случае только с вектором расположенном в этом же дочернем классе.
Может приведешь код по-подробнее. Что за класс Class используется у тебя в векторе?
Правда в одном моменте проскочила рекомендация в случае использования шаблонов - контролировать копирование и создание объектов класса
Ты, видимо, путаешь понятия шаблона и контейнера.
vector, list, queue - это контейнеры,
а шаблоны - это совершенно другое понятие, это классы объявленные как
temlate<class T, .....> class ClassName;
Интересно разобраться до конца.
Значит все было примерно так:
{
private:
typedef vector<Class> ListClass;
ListClass listclass;
};
class myClass :private myBaseClass
{
private:
typedef vector<Class> ListClass;
ListClass listclass;
};
class MyListClass
{
typedef map<myClass> ListMyClass;
ListMyClass listmyclass;
};
Т.о. в классе myClass существуют два вектора:
myClass::listclass;
и
myClass::myBaseClass::listclass;
Причем второй недоступен даже из самого класа, если в базовом он объявлен, как privat.
Если методы работы с вектором были расположены в базовом классе, то они и работать будут только с вектором расположенном в базовом классе. Методы расположенные в дочернем классе будут работать в данном случае только с вектором расположенном в этом же дочернем классе.
Может приведешь код по-подробнее. Что за класс Class используется у тебя в векторе?
Все было так:
{
protected:
typedef vector<Class> ListClass;
ListClass listclass;
};
class myClass :private myBaseClass
{
private:
typedef vector<Class> ListClass;
ListClass listclass;
};
class MyListClass
{
typedef map<myClass> ListMyClass;
ListMyClass listmyclass;
};
Собственно код класса сброшу чуть позже сегодня или завтра, а то у меня на машине сети нет, а носится сейчас с дискетами - просто нет времени.
Все функции для работы с вектором использовались из производного класса, т.ч. проблемы вродеи быть не должно...
Вот класс, объект которого собственно хотелось поместить в вектор :
#define ScladProdH
#include "ProductHigh.h"
#include "Structures.h"
class TScladProd:private TProductHigh{
public:
TScladProd();
TScladProd(const TScladProd&);
int __fastcall GetCode()const {return recordId_;}
void __fastcall SetCode(int i){recordId_=i;}
...
TDateTime __fastcall GetData3()const{return Data3;}
void __fastcall SetData3(const TDateTime& data){Data3 = data;}
TDateTime __fastcall GetTime ()const{return Time;}
void __fastcall SetTime(const TDateTime& time){Time = time;}
float __fastcall GetCount(){return Count;}
void __fastcall SetCount(const float& count){Count = count;}
//Перегруженный оператор потока для записи в файл
void __fastcall operator >> (TScladLow&);
//Перегруженный оператор присваивания, для считывания
//Запись и считывание производится через структуру фиксированного размера
void __fastcall operator = (const TScladLow& a2);
protected:
int DocCode;
int InCode;
TDateTime Data1;
TDateTime Data2;
TDateTime Data3;
TDateTime Time;
float Count;
};
bool operator < (const TScladProd&,const TScladProd&);
Существует базовый класс документа:
#define DocumentHighH
//---------------------------------------------------------------------------
#include <systdate.h>
#include <vector.h>
#include <algorithm.h>
#include "ScladProd.h"
class TDocumentHigh{
protected:
typedef vector<TScladProd>ListProd;
public:
TDocumentHigh(TDocumentLow&);
TDocumentHigh(){}
~TDocumentHigh(){}
int insertProduct(const TProductHigh& pord);
void eraseProduct(int recordId);
const TProductHigh& getProduct(int recordId)const;
...
void operator=(const TDocumentLow&);
void operator>>(TDocumentLow&);
protected:
int Code;
AnsiString Name;
TDateTime Data;
TDateTime Time;
double Amount;
int Client;
int Meneger;
int ProductCount;
bool Delete;
ListProd productlist;
};
bool operator < (const TDocumentHigh&,const TDocumentHigh&);
Часть переменных объявлена в базовом классе, они общие для всех классов продуктов.
Следующий - класс документа, который должен содержать или указатели на объекты, или массив объектов типа TScladProd:
#define InvoceInH
//---------------------------------------------------------------------------
#include "ScladProd.h"
#include "Structures.h"
class TInvoiceIn:private TDocumentHigh{
protected:
typedef vector<TScladProd>ListProd;
public:
TInvoiceIn();
TInvoiceIn(const TInvoiceIn& a2);
//Вставляет в вектор объект класса функцией push_back()
int insertProduct(const TScladProd&);
void Copy(const TInvoiceIn&);
//Возращает обект через индекс
const TScladProd getProduct(const int&)const;
...
int GetProductCount(){return ProductCount;}
void SetProductCount(const int& s) {ProductCount = s;}
void operator = (TDocumentLow&);
void operator >>(TDocumentLow&);
protected:
ListProd productlist;
};
bool operator < (const TInvoiceIn&,const TInvoiceIn&);
Функции открытого интерфейса, я убираю, т.к. они роли большой не играют и возращают занчения типа кода, имени и т.п. Все переменные объявлены в базовом классе и наследуются оттуда.
И собственно клас который работает с объектом документов выглядит вот так
#define ListDocumentH
#include <set.h>
#include <map.h>
#include "InvoceIn.h"
#include "ScladProd.h"
#include "Errors.h"
//---------------------------------------------------------------------------
class TListDocument
{
typedef multiset<TInvoiceIn>DocumentName;
typedef map<int,DocumentName::iterator>DocumentCode;
public:
TListDocument();
~TListDocument();
void __fastcall LoadFromFile(const AnsiString& f);
void __fastcall SaveToFile(const AnsiString& f);
int __fastcall insertDocument(const TInvoiceIn& doc,int recodId = 0)throw (DuplicateId);
int __fastcall Size(){return documentname.size();}
void __fastcall eraseDocument(int recordId);
void __fastcall replaseDocument(const TInvoiceIn& doc,int recordId = 0);
int __fastcall countName(const AnsiString& name,const AnsiString Packed)const;
typedef DocumentName::const_iterator c_iter;
c_iter __fastcall begin() const{return documentname.begin();}
c_iter __fastcall end() const{return documentname.end();}
c_iter __fastcall findNameStartsWith(const AnsiString& Name, const AnsiString& packed = "кг.")const;
c_iter __fastcall findNextContains(const AnsiString& searchStr,c_iter start)const;
c_iter __fastcall findId(int recordId)const;
c_iter __fastcall findMaxId()const;
c_iter __fastcall findMinId()const;
TScladProd __fastcall findProd(const int& recordId);
AnsiString __fastcall CountIn(const int&recordId);
AnsiString __fastcall CountOut(const int&recordId);
c_iter Document;
private:
TListDocumet(const TListDocument&);
TListDocument& operator=(const TListDocument&);
static int nextId;
DocumentName documentname;
DocumentCode documentcode;
void insertDocument(const TDocumentHigh& doc);
void __fastcall clearAll();
DocumentName::iterator __fastcall TListDocument::getByCode (int recordId)throw(DocumentNotFound);
};
#endif
Объект этого класса содержит список документов и список кодов документов. Код документа не может быть продублирован.
ну вот вроде бы и все. Рализация классов достаточно стандартна. Если сможешь что либо подсказать - буду благодарен. На сейчас я уже переделал все без векторов, но получается не так удобно...:(
Какой смысл в базовом классе TDocumentHigh, если все методы переопределены, и они не виртуальные. Он только вносит путаницу, например, в классе TInvoiceIn существуют два вектора:
- собственный TInvoiceIn::productlist,
- и доставшийся в наследство TInvoiceIn::TDocumentHigh::productlist.
Зачем в TListDocument используется связка multiset и map ?
TListDocument::next Id действительно должен быть статиком?
В TListDocument::insertDocument в качестве аргумента принимается ссылка на TDocumentHigh, а не на TInvoiceIn. Это может быть ошибкой.
И еще. IMHO, все operator< целесообразней внести внутрь соотв. классов.
Реализации я не выкладывал, так как не имею привычки писать комментарии - а в своей мешанине кода, я сам иногда не могу вспомнить нафига то или это. Когда приведу все в порядок - если будет интересно сброшу...
Без реализаций, конечно, сложно анализировать, но вот что я накопал.
Какой смысл в базовом классе TDocumentHigh, если все методы переопределены, и они не виртуальные. Он только вносит путаницу, например, в классе TInvoiceIn существуют два вектора:
- собственный TInvoiceIn::productlist,
- и доставшийся в наследство TInvoiceIn::TDocumentHigh::productlist.
Да никакого сбств. Пробную версию надо было сдать в понедельник...а на сегодня еще печать не реализована..:)В начале планировалось что этот класс будет чисто виртуальным, а от него будут наследовать. Потом необходимость пока отпала, а там видно будет. От веторов на сегодня я полностью отказался - и проблемой меньше стало.
Зачем в TListDocument используется связка multiset и map ?
TListDocument::next Id действительно должен быть статиком?
В TListDocument::insertDocument в качестве аргумента принимается ссылка на TDocumentHigh, а не на TInvoiceIn. Это может быть ошибкой.
Связка нужна что бы эффективно реализовать операции поиска по произвольному значению,редактирование и удаление объектов с одной стороны, и эффективный поиск и вставка по ключу, с другой стороны.
мультисет позволяет сортировать массив по произвольным полям, соответственно, эффективность поиска значительно повышается. Мап - эффективно сортирует по ключу, и ключ должен быть уникальным. При вставке генерируется исключение - не нужно искать дубликаты. Ну и еще ряд преимуществ. В паре все работает гораздо эффективние. Статическая переменная нужна что бы можно было контролировать код объекта и автоматически его присваивать. Выглядит вот так:
//если в фукцию передан объект без ИД присваиваем значение и увеличиваем статическую переменную
if(recordId ==0)recordId = nextId++;
//Просто увеличиваем значение на ИД+1, если нужно
else if(recordId >= nextId)nextId = recordId +1;
//Ключ может быть только один - потому исключаемся
else if (documentcode.count(recordId))throw DuplicateId();
TInvoiceIn *docum = new TInvoiceIn(doc);
docum->SetCode(recordId);
//Вставляем объект и получаем итератор на него
DocumentName::iterator i=documentname.insert(*docum);
//Вставляем ключ и итератор
documentcode[recordId] =i;
return recordId;
}
Прикол в том еще что собственно ни мультисет, ни мультимап ни им подобные контейнеры возращают, как ты заметил, не позицию элемента - а итератор на него...и если правило сортировки и меня не по коду(а оно таки не только по коду), то что бы найти нужный код надо крутить весь массив - т.е. линейный поиск - т.е. сам понимаешь, если три объекта - еще ладно...а если бо-о-ольше...
мап делает это чисто и эффективно...:)
И еще. IMHO, все operator< целесообразней внести внутрь соотв. классов.
Я IMHO, тоже так думал - пока не ввел...:)
Дело в том, что функции STL и контейнеров нуждаются в перегруженных операторах сравнения, но весь прикол в том, что объект функции должен получить ДВА аргумента - начальный и конечный итераторы, а фунция-член класса operator может принять только один...:) Выходов из этого положения может быть несколько каждый имеет свои достоинства и недостатки. Но для меня задача сейчас - оптимально организовать программу - после этого имеет смысл приступать к тестированию эффективности классов.
Но все таки любопытно, получается контейнер не может быть членом класса? Если есть под рукой вижуалка можно попробовать смоделировать ситуацию на ее компиляторе.
Но все таки любопытно, получается контейнер не может быть членом класса? Если есть под рукой вижуалка можно попробовать смоделировать ситуацию на ее компиляторе.
Да нет, что ты!
Вектор, как и объект любого другого класса, может быть челеном другого класса. Это 100%!
На контейнерах основаны некоторые умные указатели (smart pointers) и т.п.
Твоя проблема в реализации роботы с вектором.
Да нет, что ты!
Вектор, как и объект любого другого класса, может быть челеном другого класса. Это 100%!
На контейнерах основаны некоторые умные указатели (smart pointers) и т.п.
Твоя проблема в реализации роботы с вектором.
Ладно, позже еще вернусь к этому. Я кстати порылся в старых своих прогах, и вспомнил, что уже реализовал класс с вектором...но там немного другая задача была. Ладно посмотрим. Спасибо за участие...:)