Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / C++ [игнор отключен] [закрыт для гостей] / Предварительная инициализация ссылочной переменной абстрактного класса / 16 сообщений из 16, страница 1 из 1
23.07.2013, 14:50
    #38340619
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Доброго времени суток.

Имеется абстрактный класс Record, от которого унаследованы не виртуальные классы Comment и Shape_definition.
Я бы хотел написать примерно такой код:
Код: plaintext
1.
2.
3.
Record& record = [???]; // как-то инициализировать переменную
cin >> record;
record.draw(); // виртуальная функция, переопределённая в Comment и Shape_definition.


Я переопределил операторы >> для каждого класса. Тот, который относится к Record, предварительно выглядит так:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
std::istream& operator >> (std::istream& istr, Bushman::shp::Record& rec){
	if(!istr) throw std::runtime_error("Invalid the input stream condition.");
	char c = 0;
	istr >> c;
	if(!istr) throw std::runtime_error("Input error.");
	istr.unget();
	if(c == Bushman::shp::comment_marker){
		Bushman::shp::Comment comment;
		istr >> comment;
		rec = comment;
	}
	else if(c == Bushman::shp::header_marker){
		Bushman::shp::Shape_definition def;
		istr >> def;
		rec = def;
	}
	else{
		throw std::runtime_error("Unexpected character. The ';' or '*' expected.");
	}
	return istr;
}


Т.е. в зависимости от того, что во входном потоке, выполняется оператор >> либо для Comment, либо для Shape_definition, сохраняя полученное значение в record.

Однако, как известно, создать объект абстрактного класса нельзя. Отсюда вопрос: как бы мне правильней инициализировать Record& record? Можно, конечно попробовать как-то так:
Код: plaintext
1.
2.
3.
Record& record = Comment();
//или
Record& record = Shape_definition();


Но что-то мне этот способ не нравится... Могут ли быть какие-то подводные камни в случае использования этих вариантов?

Спасибо.
...
Рейтинг: 0 / 0
23.07.2013, 15:52
    #38340766
Анатолий Широков
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Compositum,

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
class Record {
public:
    virtual ~Record() {}
    virtual read(std::istream &in) = 0;
};

std::unique_ptr<Record> readRecord(std::istream &in) {
      // считываем из потока некоторый маркер класса
      std::string cls;
      in >> cls;
      std::unique_ptr<Record> record;
      if( cls == "comment" ) {
           record.reset(new Comment(...));
      } else {
      if( cls == "shape" ) {
           record.reset(new Shape(...));
      } else {
          throw "unknown class";
      }
      record->read(in);
      return record;
}
...
Рейтинг: 0 / 0
23.07.2013, 16:23
    #38340847
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
мне бы не хотелось использовать в данном случае указатели и сборщик мусора. Указанный мною выше способ может вызывать проблемы? Если "да", то какие?
...
Рейтинг: 0 / 0
23.07.2013, 16:25
    #38340853
MasterZiv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Compositumмне бы не хотелось использовать в данном случае указатели и сборщик мусора. Указанный мною выше способ может вызывать проблемы? Если "да", то какие?

Какой-такой сборщик мусора ?
...
Рейтинг: 0 / 0
23.07.2013, 16:27
    #38340857
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
MasterZivКакой-такой сборщик мусора ?
обыкновенный .
...
Рейтинг: 0 / 0
23.07.2013, 16:31
    #38340865
Анатолий Широков
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Compositum,

по стандарту можно инициализировать только константную ссылку временным объектом:

Код: plaintext
1.
const Record& record = Comment();



А так, я не очень понял твою задумку - ну инициализируешь ты ссылку, дальше что? ну прочтешь данные - а что после с этим объектом-то будет?
...
Рейтинг: 0 / 0
23.07.2013, 16:40
    #38340887
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Анатолий ШироковА так, я не очень понял твою задумку - ну инициализируешь ты ссылку, дальше что?
дальше читаю нужные данные из потока, назначая результат этой самой ссылке.
Анатолий Широковну прочтешь данные - а что после с этим объектом-то будет?
Ничего, будет жить, пока ссылочная переменная находится в области видимости.
...
Рейтинг: 0 / 0
23.07.2013, 16:43
    #38340896
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Анатолий ШироковА так, я не очень понял твою задумку
Задумка в том, чтобы воспользоваться полиморфизмом. Поскольку переменная является ссылкой на базовый класс, то виртуальный метод draw будет вызван на объекте реального класса. На основании маркера класса, возвращается экземпляр нужного (по контексту) производного класса.
...
Рейтинг: 0 / 0
23.07.2013, 16:47
    #38340906
Анатолий Широков
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Compositum,

Перечитал твой первый пост. Возникает единственный вопрос - а зачем ты вообще наследование используешь, если у тебя все "свалено" в Record? У тебя operator>> оперирует знаниями о природе классов Comment и Shape_definition. Тебя вот это не смущает?
...
Рейтинг: 0 / 0
23.07.2013, 16:58
    #38340935
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Анатолий ШироковУ тебя operator>> оперирует знаниями о природе классов Comment и Shape_definition. Тебя вот это не смущает?
Откровенно говоря, смущает... Я бы хотел, чтобы эти классы имели общий интерфейс, соответственно получается, что они должны иметь общего родителя. Оператор >> должен на основании маркера автоматом распознавать, экземпляр какого именно дочернего класса должен создаваться. В результате у меня получилась такая петрушка... Если у тебя есть идея, как это реализовать более грамотно - буду признателен.

Спасибо.
...
Рейтинг: 0 / 0
23.07.2013, 17:03
    #38340944
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
CompositumДоброго времени суток.

Но что-то мне этот способ не нравится... Могут ли быть какие-то подводные камни в случае использования этих вариантов?

Спасибо. Тут не подводный камень. Тут большой подводно-надводный слон. Суть полиморфизма в том что используется ССЫЛКА или УКАЗАТЕЛЬ который УКАЗЫВАЕТ на объект реализующий абстрактный интерфейс. То есть _никакого копирования_, просто _инициализация ссылки_ или _присвоение (или инициализация) указателя_.

Полиморфизм:

Код: plaintext
1.
2.
3.
Obj = Factory->CreateObj(cin); // Объект создался
Ref  ------------------->  Obj // Ссылка инициализировалась:   Object & obj = Factory->CreateObj(cin);
Ref->draw();


А вы пытаетесь сделать так:

Код: plaintext
1.
2.
3.
4.
5.
TempObj = Create();
Ref --------------------> TempObj;
Obj = Factory->CreateObj();
TempObj = Obj; // Copy Constructor WTF?
Ref ?
...
Рейтинг: 0 / 0
23.07.2013, 17:03
    #38340945
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Пока что мне приходит в голову только вынос этого кода из абстрактного класса в отдельный класс, являющийся оболочкой над потоком:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
namespace Bushman{
	namespace shp{
		class Shp_istream{
		private:
			std::istream& is;
		public:
			Shp_istream(std::istream& istr);
			Shp_istream& operator >> (Record& rec);
		};
	}
}
...
Рейтинг: 0 / 0
23.07.2013, 17:11
    #38340961
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
sherzod_,

Честно говоря, я не понял. Ссылка является константной, т.е. она указывает всегда на одну и ту же ячейку памяти, в то время как указателю можно переназначать др. адреса. Когда я назначаю ссылке новое значение, то это новое значение записывается по адресу, на который указывает ссылка, перезаписывая старое значение. Если бы я использовал указатель, но назначая ему новое значение, я бы, тем самым, назначил ему лишь другую ячейку памяти, на которую он ссылается.
...
Рейтинг: 0 / 0
23.07.2013, 17:19
    #38340984
Анатолий Широков
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Compositum,

Ну, в своем первом посте я как раз показал как бы я действовал:

1. создал бы абстрактый класс Record только с чисто виртуальными функциями - это интерфейс

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
class Record {
public:
     virtual ~Record() {}
     virtual std::istream& read(std::istream& in) = 0;
     virtual std::ostream& write(std::ostream& out) const = 0;
     virtual void draw() = 0;
};


2. создал бы конкретный наследников

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
class Comment : public Record {
      std::string comment;
public:
      override std::istream& read(std::istream& in) {
           in >> comment;
      }
      ...
};


3. создал бы фабричный метод, читающий из потока и создающий по префиксу тот или иной объект

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
std::unique_ptr<Record> readRecord(std::istream &in) {
      // считываем из потока некоторый маркер класса
      std::string cls;
      in >> cls;
      std::unique_ptr<Record> record;
      if( cls == "comment" ) {
           record.reset(new Comment(...));
      } else 
      if( cls == "shape" ) {
           record.reset(new Shape(...));
      } else {
          throw "unknown class";
      }
      record->read(in);
      return record;
}



4. ну и напоследок все это связал

Код: plaintext
1.
2.
std::unique_ptr<Record> record = readObject(std::cin);
record->draw();



Вот это и был бы чистый ООП в действии.
...
Рейтинг: 0 / 0
23.07.2013, 17:28
    #38341007
Compositum
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Анатолий Широков,

Спасибо. Я внёс изменения в грамматику. Было:

Код: sql
1.
2.
3.
4.
5.
...
(* comment block, or shape definition *)
record = { comment line } | shape definition;

shp file data = { record };


Стало так:
Код: sql
1.
2.
3.
4.
5.
...
(* comment block, or shape definition *)
record = { comment line }, [shape definition];

shp file data = { record };


Такой подход получается более простым и надёжным, чем тот, что я выбрал ранее. Исправленная версия грамматики подразумевает, что объект класса Record содержит в себе по экземпляру классов Comment и Shape_definition. Т.о. я избавился от иерархии наследования. Соответственно и фабрика не понадобится.

Всем спасибо.
...
Рейтинг: 0 / 0
23.07.2013, 21:36
    #38341354
MasterZiv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Предварительная инициализация ссылочной переменной абстрактного класса
Давай вот это вот ещё раз обсудим.

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

Всё правильно, про это тебе и говорил sherzod_ .
Присвоил -- прощай полиморфизм.

Тем более что так присваивать ссылке очень опасно -- может быть срезка.

Record& record = Comment();
// record теперь может указывать не на Record, а на его наследника.
someOtherRecord = OtherComment();
// someOtherRecord допустим указывает теперь на Record базовый.

record = someOtherRecord; // вот тут будет происходит срезка.
...
Рейтинг: 0 / 0
Форумы / C++ [игнор отключен] [закрыт для гостей] / Предварительная инициализация ссылочной переменной абстрактного класса / 16 сообщений из 16, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]