Перейти к содержимому

Как сделать ссылку на класс c

  • автор:

C++. Ссылки на объект класса. Доступ к объекту класса по ссылке. Ссылки как члены данных класса. Примеры

Ссылки на объект класса. Доступ к объекту класса по ссылке. Ссылки как члены данных класса. Примеры

Поиск на других ресурсах:

1. Как объявляется переменная, которая есть ссылкой на класс? Как объявить ссылку на объект класса?

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

Общий вид объявления ссылки на объект класса имеет вид:

CMyClass & ref = obj;
  • CMyClass – имя класса;
  • ref – имя переменной, которая есть ссылкой на объект obj ;
  • obj – имя объекта, для которого выделена память.
2. Пример объявления ссылки на объект класса и ее использование

Пусть задан класс CYear , реализующий год. Объявление класса имеет следующий вид:

// класс, реализующий категорию "Год" class CYear < int year; // внутренняя переменная public: CYear(void); // конструктор по умолчанию // конструктор с 1 параметром CYear(int year) < this->year = year; > // методы доступа int Get(void) < return year; > void Set(int year) < this->year = year; > // метод, определяющий високосный год или нет bool IsLeapYear(void); >; // конструктор по умолчанию CYear::CYear(void) < year = 2000; // начальная инициализация > // метод, определяющий високосный год или нет bool CYear::IsLeapYear(void) < if (year % 400 == 0) return true; else if (year % 100 == 0) return false; else if (year % 4 == 0) return true; else return false; >

Использование класса в другом методе

CYear c1; // c1 - объект CYear & c2 = c1; // c1 и c2 указывают на один и тот же участок памяти int d; // доступ к объекту c1 c1.Set(2010); d = c1.Get(); // d = 2010 // доступ по ссылке c2 d = c2.Get(); // d = 2010 c2.Set(3333); d = c1.Get(); // d = 3333; c1 и c2 указывают на один и тот же участок памяти

В вышеприведенном коде с помощью символа & объявляется переменная-ссылка c2 на объект класса c1 с помощью следующей строки

CYear & c2 = c1;

Для переменной c1 память уже выделена. Переменные c1 и c2 ссылаются на один и тот же участок памяти (на один и тот же объект).

3. Объявление и использование ссылки на объект класса, доступ к которому осуществляется с помощью неуправляемого ( unmanaged ) указателя. Пример

Если объявить указатель на класс ( * ) и выделить для него память, то на этот участок памяти (объект) можно объявить ссылку.
Нижеследующий пример демонстрирует использование указателя и ссылки на класс CYear, что описывается в п. 2.

CYear * pc = new CYear(); // неуправляемый указатель, выделяется память CYear & rc = (*pc); // ссылка на память, выделенную для указателя int d; // доступ к объекту по указателю pc->Set(2040); d = pc->Get(); // d = 2040 d = rc.Get(); // d = 2040 rc.Set(2100); d = pc->Get(); // d = 2100
4. Пример использования ссылки на объект класса, которая есть членом данных другого класса. Что произойдет, если в классе не инициализировать ссылку на объект?

Ссылка (переменная-ссылка) на объект класса может быть членом данных другого класса. При объявлении переменной-ссылки в классе, эта переменная может быть тут же инициализирована в специально разработанном конструкторе. В этом случае для переменной-ссылки класса память выделяется динамически (см. пример ниже).

В примере ниже демонстрируется применение класса CLine (отрезок), в котором объявляются две ссылки на класс CPoint , который описывает точку на координатной плоскости.
В классе CPoint реализованы следующие члены данных и методы:

  • внутренние переменные x , y – координаты точки;
  • конструктор по умолчанию;
  • методы доступа GetXY() , SetXY() .

В классе CLine объявляются следующие члены данных и методы:

  • внутренние переменные-ссылки типа CPoint& (ссылка на объект класса CPoint ) с именами p1 , p2 ;
  • конструктор с двумя параметрами, который динамично инициализирует ссылки p1 , p2 при объявлении объекта класса;
  • методы доступа GetPoints() , SetPoints() .

Объявление классов CPoint и CLine имеет следующий вид:

// класс, реализующий точку class CPoint < int x,y; public: // конструктор по умолчанию CPoint() < x = y = 0; >// методы доступа void GetXY(int* nx, int* ny) < *nx = x; *ny = y; >void SetXY(int nx, int ny) < x = nx; y = ny; >>; // класс, реализующий отрезок class CLine < // ссылки на объекты типа CPoint CPoint & p1; CPoint & p2; public: // конструктор по умолчанию // динамически инициализируются значения ссылок p1 и p2 CLine():p1(* new CPoint), p2(* new CPoint) < p1.SetXY(0, 0); p2.SetXY(1, 1); >// методы доступа // вернуть координаты точек void GetPoints(CPoint* pt1, CPoint* pt2) < int x, y; p1.GetXY(&x, &y); // взять значения x, y для точки p1 pt1->SetXY(x, y); // установить x,y в новую точку pt1 p2.GetXY(&x, &y); // взять x,y pt2->SetXY(x, y); // записать x,y в pt2 > // установить новые значения точек void SetPoints(CPoint* pt1, CPoint* pt2) < int x, y; // p1 => pt1 p1.GetXY(&x, &y); pt1->SetXY(x, y); // p2 => pt2 p2.GetXY(&x, &y); pt2->SetXY(x, y); > >;

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

p1' : must be initialized in constructor base/member initializer list . 

В классе CLine осуществляется динамическая инициализация ссылок p1 и p2 с помощью конструктора:

// динамическая инициализация переменных-ссылок p1, p2 класса CLine CLine():p1(* new CPoint), p2(* new CPoint) < // . >

Использование класса CLine в другом методе:

// ссылка на объекты как члены-данных класса CLine cl1; // вызывается конструктор, который инициализирует переменные-ссылки p1, p2 // дополнительные переменные CPoint point1, point2; int x, y; // проверка, как конструктор заполнил координаты точек отрезка cl1.GetPoints(&point1, &point2); point1.GetXY(&x, &y); // x = 0; y = 0 point2.GetXY(&x, &y); // x = 1; y = 1 // установить новые значения point1.SetXY(3, 8); // записать точку (3; 8) point2.SetXY(5, 9); // записать точку (5; 9) cl1.SetPoints(&point1, &point2); // проверка CPoint pp1, pp2; cl1.GetPoints(&pp1, &pp2); pp1.GetXY(&x, &y); // x = 3; y = 8 pp2.GetXY(&x, &y); // x = 5; y = 9

Связанные темы

  • Объекты класса как члены данных класса. Примеры
  • Понятие класса. Объявление класса. Объект класса. Классы в среде CLR. Инкапсуляция данных в классе

Как сделать ссылку на класс в С++

Как можно реализовать функцию getClassOf? И можно ли вообще?

Отслеживать

задан 2 июн 2021 в 9:28

Александр С Александр С

using Class_id = B; , но скорее всего Вам нужны шаблоны

2 июн 2021 в 9:33

А если значение надо будет поменять?

2 июн 2021 в 9:34

похоже Вы пытаетесь сделать фабрику.

2 июн 2021 в 9:39

¿А почему бы не написать просто B b; ? Из вопроса совершенно непонятно, где и как вам могла бы понадобится прослойка в виде Class_id , чем бы оно не было. Так что приводите более-менее полноценный пример использования. И заметьте, что С++ является языком со статической компиляцией и тип объекта b задается строго во время компиляции, а не во время выполнения.

2 июн 2021 в 9:40

Нельзя. В таком случае придется делать либо иерархию классов, наследующих от какого-то базового абстрактного типа и создавать объекты динамически (то бишь уже упомянутый паттерн фабрика), либо использовать ::std::variant или его аналоги. ЗЫ в предыдущем комментарии «со статической компиляцией» следует читать «со статической типизацией»

Как создать ссылку на класс

Как в функции my_function преобразовать ссылку на базовый класс в ссылку на класс B или С
Всем привет, как в функции my_function преобразовать ссылку на базовый класс в ссылку на класс B.

Как в порождаемый класс передать ссылку на основной класс?
Основной класс: #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include.

Как вернуть ссылку на класс
Здравствуйте, есть класс и нужно вернуть на него ссылку, а затем перегрузить hashCode() .

Как получить ссылку на класс объекта?
У меня есть контрол Panel класса MyToolTip. Как можно получить ссылку на MyToolTip через Panel? .

2184 / 1254 / 143
Регистрация: 28.04.2010
Сообщений: 4,592

Лучший ответ

Сообщение было отмечено dondublon как решение

Решение

встречный вопрос, а зачем это нужно? В c# объект класса это уже ссылочный тип
т.е.

1 2 3 4 5
public class MyClass { // . } MyClass mc = new MyClass(); // в данном случаи mc это ссылка на область памяти

Эксперт Python

4615 / 2036 / 359
Регистрация: 17.03.2012
Сообщений: 10,102
Записей в блоге: 6

Отвечаю.
Так я могу передать для создания ссылку на класс-наследник, причем создающий не будет знать о наследнике и его специфике. Получается такая мини-фабрика. Нужно для разделения кусков логики. В виде кода выглядит так:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
// просто обычный класс. // создающий - о нём знает. TMyClass = class constructor CReate(a: string; b: integer); end; // наследник. о нём создающий уже не знает. TMyInherit = class(TMyClass) end; // ссылка на этот класс TMyClassReference = class of TMyCLass; // создает наши классы: TWorker = class Fclass: TMyClassReference; procedure SetCustomClass(AValue: TMyClassReference); function MakeSomeClass(): boolean; end; . function TWorker.MakeSomeClass: boolean; var working : TMyClass; begin working := FCLass.Create('it works', 23); result := true; end; procedure TWorker.SetCustomClass(AValue: TMyClassReference); begin Fclass := AValue; end;

Я подаю в SetCustomClass как аргумент TMyInherit, после этого в MakeSomeClass у меня будет создаваться он, хотя TWorker про TMyInherit не знает.

Добавлено через 6 минут
В принципе, я нашел более-менее схожий функционал, но уж больно громоздко получается.

1 2 3
var t = typeof(DividerDraft); // это мой тип, его я могу передать var ci = t.GetConstructor(new Type[3]{ typeof(int[,]), typeof(DividerData), typeof(float)}); // а это сцылко на его конструктор с тремя параметрами. могу и его передать, как вариант.

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

Добавлено через 4 минуты
Если кому интересно — вот, чувак провентилировал вопрос:
http://metaclass.livejournal.com/271563.html

87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
Помогаю со студенческими работами здесь

Как передать ссылку на объект в класс
Здравствуйте! Подскажите, пожалуйста, почему не работает: log.h class LogClass < public.

Как передать ссылку по клику в другой класс?
как передать ссылку по клику в другой класс есть класс парсера вот часть кода protected.

Как правильно передать ссылку в другой класс
Добрый день! Есть класс class ViewController: UIViewController, UITableViewDataSource.

Как получить ссылку на класс Page из .dll сборки
Как можно получить ссылку на класс Page, использующий сборку .dll? Например, если из сборки надо.

Как сделать ссылку на класс c

Здравствуйте, VladEC, Вы писали:

VEC>Столкнулся с кодом, который кратко можно представить в следующем виде (оставил только значимые для вопроса поля и методы):

VEC>class FactoryA; VEC>class A VEC> < VEC>public: VEC> A(FactoryA&);

Конструктор с одним параметром другого типа должен быть explicit, чтобы не было неявного приведения из FactoryA& к A.

Следует рассмотреть возможность передачи фабрики по ссылке на неконстанту.

Следует задуматься о правильности применения терминологии и об архитектуре решения. Обычно фабрика — это вот так:

class A; typedef boost::shared_ptr APtr; class AFactory < public: APtr CreateA(…) const < return boost::make_shared(…); > >;
VEC>> VEC>class WrapperA VEC> < VEC>public: VEC> WrapperA(FactoryA& factory) :

Конструктор с одним параметром другого типа должен быть explicit, чтобы не было неявного приведения из FactoryA& к WrapperA.

VEC> m_A(*new A(factory) VEC> 

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

VEC> > VEC> ~WrapperA() VEC> < VEC>delete &m_A; VEC> >

Нарушен закон Большой Тройки: если у класса явно определён деструктор, конструктор копирования и/или операция присваивания, то у него должны быть явно определены все три. В данном случае, например, автосгенерированный конструктор копирования скопирует ссылку на подобъект, и получится, что два целых ссылаются на одну и ту же часть. При уничтожении любого из целых часть тоже удалится, а оставшееся в живых целое будет ссылаться на труп части. При удалении второго целого произойдёт попытка удалить часть повторно, со всеми последствиями.

Операция присваивания же вообще не сможет сгенерироваться, потому что ссылки нельзя (пере)присваивать.

VEC>private: VEC> A& m_A; VEC>> VEC>

VEC>Вопросы:
VEC>1) Есть ли другие очевидные проблемы в коде помимо разыменования *new без проверки результата выделения памяти и отсутствия обработки исключений?

Этой проблемы на самом деле нет. Класс WrapperA реагирует на недостаток памяти выбросом исключения std::bad_alloc. Ресурсы при этом не текут.

VEC>2) Высвобождение объекта по адресу от ссылки — это нормально? Вроде бы все правильно, должны получить адрес нужного объекта, но в первый раз такое вижу.

Так делать можно, но некрасиво. Операнд delete, как правило, должен быть переменной-указателем, а не сложным выражением.

Вообще же, хорошие высокоуровневые классы не вызывают явно new и delete — вместо этого используются умные указатели.

VEC>3) Кто-нибудь еще прифигел от такого кода или так пишется современный Си++ код?
VEC>Я в основном на Си писал, работал с malloc()/free() и честно проверял результаты вызовов и "нулевость" указателей при входе в функции.

В С у тебя нет исключений, поэтому неудачу выделения памяти приходится проверять. В C++ неудача памяти приводит к исключению std::bad_alloc, поэтому проверять необязательно. (Но можно, если целый объект может осмысленно существовать без части.)

VEC>Т.е. я бы хранил в классе-обертке обычный указатель, а не ссылку, проинициализировал его нулем, выделил память с отловом возможных исключений от new, проверил результат и после этого присвоил указателю адрес.

Избыточно (но см. уточнение в скобках выше).

VEC>P.S. Лично я ссылки не люблю. Из-за совпадения синаксиса при работе с копией объекта и со ссылкой на объект появляется возможность непреднамеренного изменения состояния объекта-ссылки.
VEC>Есть места, где они необходимы (перегруженные операторы, например), но не понимаю, зачем пихать их везде, где ни попадя.

Это банально удобно.

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

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

Re[2]: Использование ссылок в качестве полей класса

От: VladEC
Дата: 15.06.11 08:21
Оценка:

Здравствуйте, Tilir, Вы писали:

VEC>>1) Есть ли другие очевидные проблемы в коде помимо разыменования *new без проверки результата выделения памяти и отсутствия обработки исключений?
T>Код написан человеком, который считает себя умнее компилятора.
T>Если упростить его до:
T>

T>class FactoryA; T>class A T> < T>public: T> A(FactoryA&); T>> T>class WrapperA T> < T>public: T> WrapperA(FactoryA factory) : T> m_A(factory) T> < T>> T> ~WrapperA() T> < T>> T>private: T> A m_A; T>> T>

T>то компилятор сам выделит и очистит память под временный объект.
Есть одна проблема. Я забыл упомянуть одну немаловажную вещь: class A — абстрактный
То есть интерфейс с частично реализованными функциями. Он с помощью переданной фабрики создает конкретный класс, реализующий его интерфейс.
Соответственно, полем другого класса может быть только указатель или ссылка на него.
Насколько я все это понимаю, код имел целью воплостить идиому PIMPL (указатель на реализацию), но с "удобным" представлением объекта в виде ссылки, а не указателя.
По-хорошему, должен быть закрыт конструктор копирования и оператор присваивания. Объект класса А должен существовать в единственном экземпляре.

T>Я бы тут с другого офигел:
T>

T> A(FactoryA& factory); T>

T>Семантически означает что объект используемый для инициализации может измениться в процессе инициализации. А вот это уже ой. До свидания контейнеры STL, например.

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

Re[2]: Использование ссылок в качестве полей класса

От: VladEC
Дата: 15.06.11 08:51
Оценка:

Спасибо за развернутый ответ.
Сразу отмечу, что я забыл упомянуть в первом посте.
1) класс А — абстрактный. С помощью FabricaA создается конкретный класс, реализующий интефрейс А. Поэтому полем другого класса может быть только ссылка или указатель на А.
2) целевые платформы включают встраиваемые, на некоторых из которых не поддерживаются исключения и STL обходится слишком дорого. По крайней мере boost портирован не на все.
3) код проекта exception-less. Кода много мегабайт, исправлять поздно.
4) WrapperA, похоже, реализует PIMPL, но не очень понятно, зачем делать PIMPL для абстрактного интерфейса. Вы не можете подсказать?

C>Следует задуматься о правильности применения терминологии и об архитектуре решения. Обычно фабрика — это вот так:
C>

C>class A; C>typedef boost::shared_ptr APtr; C>class AFactory C>< C>public: C> APtr CreateA(…) const < return boost::make_shared(…); > C>>; C>
 Ну я обычно примерно так и пишу. Только чаще обычный указатель возвращаю, который вызывающий код запихивает в "умный" нужного типа. Хотя для singleton, возвращать, видимо, надо именно именно умный указатель, считающий ссылки. VEC>>class WrapperA VEC>>< VEC>> public: VEC>> WrapperA(FactoryA& factory) : C>

Конструктор с одним параметром другого типа должен быть explicit, чтобы не было неявного приведения из FactoryA& к WrapperA.

FactoryA и WrapperA сильно разнятся, чтобы компилятор осилил неявное приведение, но спасибо, учту.
Попробую поправить.

C>

VEC>> m_A(*new A(factory) VEC>>

Если здесь потом окажется ещё какой-то код, могущий когда-нибудь выбросить исключение, то выделенная под m_A память утечёт. Поскольку время жизни подобъекта в этом классе всегда совпадает с временем жизни целого, то стоит хранить подобъект не по ссылке и не по указателю, а непосредственно как член целого. Тогда и возни с динамическим выделением не будет.
C>

 Проблема, как я уточнил, в том, что А - абстрактный интерфейс. В проекте есть классы с несколькими полями-ссылками, инициализируемыми подобным же образом. Так что не в этом классе, но в других память вполне потечет при ошибках распределения памяти. VEC>> > VEC>> ~WrapperA() VEC>> < VEC>> delete &m_A; VEC>> > C>

Нарушен закон Большой Тройки: если у класса явно определён деструктор, конструктор копирования и/или операция присваивания, то у него должны быть явно определены все три. В данном случае, например, автосгенерированный конструктор копирования скопирует ссылку на подобъект, и получится, что два целых ссылаются на одну и ту же часть. При уничтожении любого из целых часть тоже удалится, а оставшееся в живых целое будет ссылаться на труп части. При удалении второго целого произойдёт попытка удалить часть повторно, со всеми последствиями.
Спасибо, что напомнили. Правда, в данном проекте не допускается копирования объекта А/WrapperA, т.е. копирующий конструктор и оператор присаваивания должны быть закрытыми и без реализации.
C>Операция присваивания же вообще не сможет сгенерироваться, потому что ссылки нельзя (пере)присваивать.
Ну оно же по умолчанию копирует класс байт-в-байт, так что может и получится.

VEC>>Вопросы:
VEC>>1) Есть ли другие очевидные проблемы в коде помимо разыменования *new без проверки результата выделения памяти и отсутствия обработки исключений?
C>Этой проблемы на самом деле нет. Класс WrapperA реагирует на недостаток памяти выбросом исключения std::bad_alloc. Ресурсы при этом не текут.

А если исключения не поддерживаются компилятором или выключены?
И не будет ли правильно ловить исключения по месту:

WrapperA(FactoryA& factory) try: m_A(*new A(factory)) <> catch(. ) <>

Оно, конечно, пробрасывается дальше, но можно высвободить ресурсы, выделенные без ошибки.

VEC>>3) Кто-нибудь еще прифигел от такого кода или так пишется современный Си++ код?
VEC>>Я в основном на Си писал, работал с malloc()/free() и честно проверял результаты вызовов и "нулевость" указателей при входе в функции.
C>В С у тебя нет исключений, поэтому неудачу выделения памяти приходится проверять. В C++ неудача памяти приводит к исключению std::bad_alloc, поэтому проверять необязательно. (Но можно, если целый объект может осмысленно существовать без части.)
А что делать в exception-less коде или на платформе без поддержки исключений?
Я так понимаю C-style более универсальный в данном случае.

VEC>>Т.е. я бы хранил в классе-обертке обычный указатель, а не ссылку, проинициализировал его нулем, выделил память с отловом возможных исключений от new, проверил результат и после этого присвоил указателю адрес.
C>Избыточно (но см. уточнение в скобках выше).
Сложно привыкнуть к try-catch, если работаешь на встраиваемых платформах

VEC>>P.S. Лично я ссылки не люблю. Из-за совпадения синаксиса при работе с копией объекта и со ссылкой на объект появляется возможность непреднамеренного изменения состояния объекта-ссылки.
VEC>>Есть места, где они необходимы (перегруженные операторы, например), но не понимаю, зачем пихать их везде, где ни попадя.
C>Это банально удобно.
Вместо -> ставится ., что облегчает написание, но иногда мешает правильному чтению кода. Ссылка не может меняться и быть нулевой, ну так это легко решается правильной организацией кода и модификатором * const.
Я просто не очень понимаю, в чем именно удобство.

C>Но да, ссылки лучше не использовать как члены классов. Потому что класс с членом-ссылкой сразу же лишается возможности автоматически быть CopyAssignable, да и вообще реализация операции присваивания будет сложной.
Интересно, а не-автоматически как присваивание реализовать?
В голову приходит только явная инициализация ссылок в списке инициализации копирующего конструктора/оператора присваивания.

Re[3]: Использование ссылок в качестве полей класса

От: Centaur
Дата: 15.06.11 11:08
Оценка:

Здравствуйте, VladEC, Вы писали:

VEC>2) целевые платформы включают встраиваемые, на некоторых из которых не поддерживаются исключения и STL обходится слишком дорого. По крайней мере boost портирован не на все.

Тогда не надо говорить, что в проекте используется C++. Без исключений это уже какой-то другой язык.

VEC>3) код проекта exception-less. Кода много мегабайт, исправлять поздно.

Если это всё-таки C++, но без использования исключений, то вместо new везде должно быть new(std::nothrow) и проверки на то, что память выделилась. Кроме того, придётся думать о том, что делать с объектами, у которых при конструировании не выделилась память.

C>>Конструктор с одним параметром другого типа должен быть explicit, чтобы не было неявного приведения из FactoryA& к WrapperA.

VEC>FactoryA и WrapperA сильно разнятся, чтобы компилятор осилил неявное приведение, но спасибо, учту.

Дело не в том, что он осилит. Кто-то случайно напишет f(factory), где функция f принимает WrapperA, и компилятор радостно создаст экземпляр WrapperA, передав factory в конструктор. А потом, после возврата из f, так же радостно этот временный объект убьёт.

C>>Операция присваивания же вообще не сможет сгенерироваться, потому что ссылки нельзя (пере)присваивать.
VEC>Ну оно же по умолчанию копирует класс байт-в-байт, так что может и получится.

Не байт-в-байт, а почленно. А для констант и ссылок присваивание не определено.

VEC>>>Вопросы:
VEC>>>1) Есть ли другие очевидные проблемы в коде помимо разыменования *new без проверки результата выделения памяти и отсутствия обработки исключений?
C>>Этой проблемы на самом деле нет. Класс WrapperA реагирует на недостаток памяти выбросом исключения std::bad_alloc. Ресурсы при этом не текут.

VEC>А если исключения не поддерживаются компилятором или выключены?

Тогда new без std::nothrow или других параметров не должно компилироваться.

VEC>И не будет ли правильно ловить исключения по месту:

VEC>WrapperA(FactoryA& factory) try: VEC> m_A(*new A(factory)) VEC><> VEC>catch(. ) VEC><>

VEC>Оно, конечно, пробрасывается дальше, но можно высвободить ресурсы, выделенные без ошибки.

Я не представляю, зачем так делать. В компиляторе без исключений ловить нечего. А в компиляторе с исключениями всё успевшее сконструироваться уничтожится автоматически в правильном порядке.

VEC>>>Я в основном на Си писал, работал с malloc()/free() и честно проверял результаты вызовов и "нулевость" указателей при входе в функции.
C>>В С у тебя нет исключений, поэтому неудачу выделения памяти приходится проверять. В C++ неудача памяти приводит к исключению std::bad_alloc, поэтому проверять необязательно. (Но можно, если целый объект может осмысленно существовать без части.)
VEC>А что делать в exception-less коде или на платформе без поддержки исключений?
VEC>Я так понимаю C-style более универсальный в данном случае.

Я не представляю, как писать на С++ без исключений. Там ломаются все преимущества C++ над C.

C>>Но да, ссылки лучше не использовать как члены классов. Потому что класс с членом-ссылкой сразу же лишается возможности автоматически быть CopyAssignable, да и вообще реализация операции присваивания будет сложной.
VEC>Интересно, а не-автоматически как присваивание реализовать?
VEC>В голову приходит только явная инициализация ссылок в списке инициализации копирующего конструктора/оператора присваивания.

У оператора присваивания не может быть списка инициализации. Но может быть осмысленно вызвать присваивание для объекта, на который ссылается ссылка. Или может быть осмысленно пропустить присваивание объекта, на который ссылается ссылка.

Re[3]: Использование ссылок в качестве полей класса

От: wander
Дата: 15.06.11 11:22
Оценка: 6 (1) +1

Здравствуйте, VladEC, Вы писали:

VEC>Ну оно же по умолчанию копирует класс байт-в-байт, так что может и получится.
Это не верно. По умолчанию используется почленное копирование, а не нечто вроде memcpy.

VEC>А если исключения не поддерживаются компилятором или выключены?
А этот случай, именно такой?

VEC>>>3) Кто-нибудь еще прифигел от такого кода или так пишется современный Си++ код?
Нет, этот код — выпендреж чистой воды.

VEC>>>Я в основном на Си писал, работал с malloc()/free() и честно проверял результаты вызовов и "нулевость" указателей при входе в функции.
VEC>А что делать в exception-less коде или на платформе без поддержки исключений?
Не ипользовать исключения. Если планируется использовать код на разных системах c поддержкой исключений и без, конкретно для new есть форма new(std::nothrow).

VEC>>>P.S. Лично я ссылки не люблю. Из-за совпадения синаксиса при работе с копией объекта и со ссылкой на объект появляется возможность непреднамеренного изменения состояния объекта-ссылки.
VEC>>>Есть места, где они необходимы (перегруженные операторы, например), но не понимаю, зачем пихать их везде, где ни попадя.
Мне кажется, что нужно избавляться от эмоциональных оценок иструментов. Это мешает.

VEC>Я просто не очень понимаю, в чем именно удобство.
Здесь вопрос скорее не удобства, я бы сказал, а семантики. Если нам нужна семантика объекта, то используем ссылки, если нужны семантика указателя — то указатели. Инами словами, если от ссылки требуется быть нулевой или менять значение в процессе — то это должен быть указатель. В остальных случаях — ссылка.

Re[4]: Использование ссылок в качестве полей класса

От: VladEC
Дата: 15.06.11 12:28
Оценка:

VEC>>2) целевые платформы включают встраиваемые, на некоторых из которых не поддерживаются исключения и STL обходится слишком дорого. По крайней мере boost портирован не на все.
C>Тогда не надо говорить, что в проекте используется C++. Без исключений это уже какой-то другой язык.

Ну зачем же так категорично?
Есть достаточно много embedded-компиляторов, не поддерживающих исключения и rtti, но при этом частично реализующих С++.
Такой Си с классами получается

VEC>>3) код проекта exception-less. Кода много мегабайт, исправлять поздно.
C>Если это всё-таки C++, но без использования исключений, то вместо new везде должно быть new(std::nothrow) и проверки на то, что память выделилась. Кроме того, придётся думать о том, что делать с объектами, у которых при конструировании не выделилась память.

Видимо, в компиляторе без поддержки исключений std::nothrow ставится по умолчанию.
А для случаев не полного выделения памяти под поля класса, видимо, фабричные методы и классы пригождаются. Создают объект, проверяют, при некорректной инициализации удаляют и возвращают 0.

C>>>Операция присваивания же вообще не сможет сгенерироваться, потому что ссылки нельзя (пере)присваивать.
VEC>>Ну оно же по умолчанию копирует класс байт-в-байт, так что может и получится.
C>Не байт-в-байт, а почленно. А для констант и ссылок присваивание не определено.

Хех. Си подводит
Там чаще всего именно memcpy.

VEC>>>>Вопросы:
VEC>>>>1) Есть ли другие очевидные проблемы в коде помимо разыменования *new без проверки результата выделения памяти и отсутствия обработки исключений?
C>>>Этой проблемы на самом деле нет. Класс WrapperA реагирует на недостаток памяти выбросом исключения std::bad_alloc. Ресурсы при этом не текут.
VEC>>А если исключения не поддерживаются компилятором или выключены?
C>Тогда new без std::nothrow или других параметров не должно компилироваться.

Компилируется
У gcc вообще есть опция -fno-exceptions. Код при ее включении переписывать не требуется.

VEC>>И не будет ли правильно ловить исключения по месту:
C>

VEC>>WrapperA(FactoryA& factory) try: VEC>> m_A(*new A(factory)) VEC>><> VEC>>catch(. ) VEC>><> C>

VEC>>Оно, конечно, пробрасывается дальше, но можно высвободить ресурсы, выделенные без ошибки.
C>Я не представляю, зачем так делать. В компиляторе без исключений ловить нечего. А в компиляторе с исключениями всё успевшее сконструироваться уничтожится автоматически в правильном порядке.

Что, и динамическая память высвободится??
Без умных указателей?

VEC>>>>Я в основном на Си писал, работал с malloc()/free() и честно проверял результаты вызовов и "нулевость" указателей при входе в функции.
C>>>В С у тебя нет исключений, поэтому неудачу выделения памяти приходится проверять. В C++ неудача памяти приводит к исключению std::bad_alloc, поэтому проверять необязательно. (Но можно, если целый объект может осмысленно существовать без части.)
VEC>>А что делать в exception-less коде или на платформе без поддержки исключений?
VEC>>Я так понимаю C-style более универсальный в данном случае.
C>Я не представляю, как писать на С++ без исключений. Там ломаются все преимущества C++ над C.

Не все
Иначе на embedded не тащили бы С++. При этом далеко не все компиляторы поддерживают даже шаблоны, не то, что исключения. Rtti вообще не поддерживается массово.
Т.е. платформенно переносимый код должен быть ближе к С, чем к С++.

Re[4]: Использование ссылок в качестве полей класса

От: VladEC
Дата: 15.06.11 12:38
Оценка:

Здравствуйте, wander, Вы писали:

VEC>>Ну оно же по умолчанию копирует класс байт-в-байт, так что может и получится.
W>Это не верно. По умолчанию используется почленное копирование, а не нечто вроде memcpy.

В Си копировалось, насколько я знаю/видел. Забыл, что в С++ с конструкторами для всего и вся оно поменялось.

VEC>>А если исключения не поддерживаются компилятором или выключены?
W>А этот случай, именно такой?

Да.

VEC>>>>3) Кто-нибудь еще прифигел от такого кода или так пишется современный Си++ код?
W>Нет, этот код — выпендреж чистой воды.

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

VEC>>>>Я в основном на Си писал, работал с malloc()/free() и честно проверял результаты вызовов и "нулевость" указателей при входе в функции.
VEC>>А что делать в exception-less коде или на платформе без поддержки исключений?
W>Не ипользовать исключения. Если планируется использовать код на разных системах c поддержкой исключений и без, конкретно для new есть форма new(std::nothrow).

Я правильно понимаю, что оно явно отключает исключения в new() в системе с их поддержкой?
Т.е. пишется, чтобы не полагаться на ключи компилятора, отключающие поддержку исключенй.

VEC>>>>P.S. Лично я ссылки не люблю. Из-за совпадения синаксиса при работе с копией объекта и со ссылкой на объект появляется возможность непреднамеренного изменения состояния объекта-ссылки.
VEC>>>>Есть места, где они необходимы (перегруженные операторы, например), но не понимаю, зачем пихать их везде, где ни попадя.
W>Мне кажется, что нужно избавляться от эмоциональных оценок иструментов. Это мешает.

Учту
Большая часть моего кода вообще на Си (цифровая обработка) + фреймворки на С++. При таком раскладе на стыке интерфейсов C/C++ ссылки неприменимы без оберток.
Соответственно, ссылки приводят к лишнему коду при отсутствии удобств (проверки на 0 из Си-кода не убираю).

VEC>>Я просто не очень понимаю, в чем именно удобство.
W>Здесь вопрос скорее не удобства, я бы сказал, а семантики. Если нам нужна семантика объекта, то используем ссылки, если нужны семантика указателя — то указатели. Инами словами, если от ссылки требуется быть нулевой или менять значение в процессе — то это должен быть указатель. В остальных случаях — ссылка.

Поле объекта делать ссылкой тоже нежелательно, видимо?
Видел эти рекомендации в чуть более размазанном варианте, спасибо за выжимку.

Re[5]: Использование ссылок в качестве полей класса

От: wander
Дата: 15.06.11 15:37
Оценка:

Здравствуйте, VladEC, Вы писали:

VEC>>>А если исключения не поддерживаются компилятором или выключены?
W>>А этот случай, именно такой?
VEC>Да.
Я бы в таком случае все равно код писал так, чтобы если исключения в системе вдруг появились, то ничего не поломалось бы. Просто не использовать их в коде явно. А так ведь есть полно техник обеспечения безопасности исключений, которые не требует использования ключевых слов try-catch и throw.

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

VEC>Я правильно понимаю, что оно явно отключает исключения в new() в системе с их поддержкой?
Да.
VEC>Т.е. пишется, чтобы не полагаться на ключи компилятора, отключающие поддержку исключенй.
Это специальная форма new, которая возвращает ноль, если память не удалось выделить. Обычный new в современных компиляторах ноль не возвращает.

VEC>Поле объекта делать ссылкой тоже нежелательно, видимо?
Зависит от ситуации. В большинстве случаев это ни к чему.

Re[5]: Использование ссылок в качестве полей класса

От: Centaur
Дата: 15.06.11 17:27
Оценка:

Здравствуйте, VladEC, Вы писали:

C>>В компиляторе без исключений ловить нечего. А в компиляторе с исключениями всё успевшее сконструироваться уничтожится автоматически в правильном порядке.

VEC>Что, и динамическая память высвободится??
VEC>Без умных указателей?

Зачем писать без умных указателей, когда можно писать с умными указателями?

Re[6]: Использование ссылок в качестве полей класса

От: VladEC
Дата: 15.06.11 19:45
Оценка:

Здравствуйте, wander, Вы писали:

VEC>>>>А если исключения не поддерживаются компилятором или выключены?
W>>>А этот случай, именно такой?
VEC>>Да.
W>Я бы в таком случае все равно код писал так, чтобы если исключения в системе вдруг появились, то ничего не поломалось бы. Просто не использовать их в коде явно. А так ведь есть полно техник обеспечения безопасности исключений, которые не требует использования ключевых слов try-catch и throw.
Э-э-э. Придется искать техники
Я один только способ смог сходу придумать, озвучивать не буду
VEC>>Спасибо. Я думал, совсем ничего не понимаю в этой жизни.
VEC>>Привык, что чем проще, тем надежнее.
W>Простота понятие относительное. Я о том, что в коде выше нет ничего сложного. Он даже не запутывает. Просто он странный. А в целом ты прав конечно.
Код запутал, пока я не нашел заголовочный файл
Больше того, глядя только на конструктор, я решил, что память, выделенная new и тут же разыменованная, утечет, потому что созданный объект передается в конструктор копирования, там копируется и после этого "подвисает" без удаления.

Конструктор обертки был определен в .cpp-файле с именем, не совпадающим с заголовочным файлом, в котором был определен сам класс обертки.
Пришлось воспользоваться поиском.

VEC>>Я правильно понимаю, что оно явно отключает исключения в new() в системе с их поддержкой?
W>Да.
VEC>>Т.е. пишется, чтобы не полагаться на ключи компилятора, отключающие поддержку исключенй.
W>Это специальная форма new, которая возвращает ноль, если память не удалось выделить. Обычный new в современных компиляторах ноль не возвращает.

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

VEC>>Поле объекта делать ссылкой тоже нежелательно, видимо?
W>Зависит от ситуации. В большинстве случаев это ни к чему.

Сходу могу придумать только передачу в конструктор и хранение ссылок на внешние объекты.
Впрочем, может быть в сложных системах и другие ситуации бывают.

Re[6]: Использование ссылок в качестве полей класса

От: VladEC
Дата: 15.06.11 19:47
Оценка:

Здравствуйте, Centaur, Вы писали:

C>>>В компиляторе без исключений ловить нечего. А в компиляторе с исключениями всё успевшее сконструироваться уничтожится автоматически в правильном порядке.
VEC>>Что, и динамическая память высвободится??
VEC>>Без умных указателей?
C>Зачем писать без умных указателей, когда можно писать с умными указателями?

Сказать сложно, однако код имеет место быть.
Большой, тяжелый проект, правленный минимум двумя десятками человек.

Re[7]: Использование ссылок в качестве полей класса

От: wander
Дата: 15.06.11 22:16
Оценка: 6 (1)

Здравствуйте, VladEC, Вы писали:

VEC>Здравствуйте, wander, Вы писали:

VEC>>>>>А если исключения не поддерживаются компилятором или выключены?
W>>>>А этот случай, именно такой?
VEC>>>Да.
W>>Я бы в таком случае все равно код писал так, чтобы если исключения в системе вдруг появились, то ничего не поломалось бы. Просто не использовать их в коде явно. А так ведь есть полно техник обеспечения безопасности исключений, которые не требует использования ключевых слов try-catch и throw.
VEC>Э-э-э. Придется искать техники
VEC>Я один только способ смог сходу придумать, озвучивать не буду
RAII.

VEC>А что же он возвращает.
VEC>Если память выделить не удалось, а код компилируется без поддержки исключений.
VEC>Как бы попробовать.
А ничего не возвращает. То, что ты отключил исключения в своем проекте не означает, что ты отключил их везде. Если мы говорим про ключик fno-exceptions, то исключение все равно будет генерироваться new. Только вот поймать его ты уже не сможешь.

Скомпилируй с ключиком -fno-exceptions

#include #include int main() < char * p = new char[std::numeric_limitsunsigned long>::max()]; if(!p) < printf("not allocated\n"); > delete[] p; >

Re[3]: Использование ссылок в качестве полей класса

От: Tilir http://tilir.livejournal.com
Дата: 15.06.11 23:15
Оценка:

Здравствуйте, VladEC, Вы писали:

VEC>Есть одна проблема. Я забыл упомянуть одну немаловажную вещь: class A — абстрактный
VEC>То есть интерфейс с частично реализованными функциями. Он с помощью переданной фабрики создает конкретный класс, реализующий его интерфейс.

Тогда как его экземпляр может быть создан через new?

Re[2]: Использование ссылок в качестве полей класса

От: rg45
Дата: 16.06.11 04:46
Оценка:

Здравствуйте, Tilir, Вы писали:

T>Код написан человеком, который считает себя умнее компилятора.
T>Если упростить его до:

T>

T>class FactoryA; T>class A T> < T>public: T> A(FactoryA&); T>> T>class WrapperA T> < T>public: T> WrapperA(FactoryA factory) : T> m_A(factory) T> < T>> T> ~WrapperA() T> < T>> T>private: T> A m_A; T>> T>

T>то компилятор сам выделит и очистит память под временный объект.

Иногда на косвенное агрегирование идут вполне осмысленно — в целях уменьшения связности классов и заголовочных файлов. А иногда агрегируемый тип полезно вовсе спрятать, от греха подальше: The Pimpl Idiom

--
C++ уже затем учить надо, что он ум в порядок приводит. (с) Ломоносов М.В.
Re[4]: Использование ссылок в качестве полей класса

От: VladEC
Дата: 16.06.11 09:29
Оценка:

Здравствуйте, Tilir, Вы писали:

T>Здравствуйте, VladEC, Вы писали:

VEC>>Есть одна проблема. Я забыл упомянуть одну немаловажную вещь: class A — абстрактный
VEC>>То есть интерфейс с частично реализованными функциями. Он с помощью переданной фабрики создает конкретный класс, реализующий его интерфейс.

T>Тогда как его экземпляр может быть создан через new?

Вот в том и прикол, что создается не его экземпляр, а экземпляр производного класса.
А этот абстрактный класс — ссылка/указатель, что допустимо.
В общем для меня осталась одна загадка: зачем заворачивать в PIMPL абстрактный интерфейс, который и так скрывает детали реализации.
Достаточно было сделать фабричный метод, возвращающий указатель на абстрактный интерфейс.

Re[3]: Использование ссылок в качестве полей класса

От: VladEC
Дата: 16.06.11 09:53
Оценка:

Здравствуйте, rg45, Вы писали:

R>Иногда на косвенное агрегирование идут вполне осмысленно — в целях уменьшения связности классов и заголовочных файлов. А иногда агрегируемый тип полезно вовсе спрятать, от греха подальше: The Pimpl Idiom

Не бейте меня, пожалуйста
Я слишком сильно упростил исходный код т.к. меня интересовало, насколько обоснованно использовать ссылку в качестве поля класса, и что будет, если проект написан без поддержки исключений. Вообще качество такого подхода и его последствия.
На самом деле класс А абстрактный, а реализация конструктора обертки WrapperA создает конкретный класс, наследующий его интерфейс, и присавивает его ссылке.
Т.е. есть разумное объяснение, почему класс А в исходном коде нельзя сделать просто членом оберточного класса.

Более полный пример включает интерфейсный класс и приведен ниже.
Глядя на него, я не понимаю, зачем обертывать абстрактный интерфейс, который уже скрывает детали реализации, в некое подобие Pimpl.
Видимо, на самом деле это хитрая реализация фабричного класса.
Я в таких случаях просто описываю интерфейс и (фабричные) методы, возвращающие указатель на этот интерфейс.
Реализация фабричных методов привязана к конкретным классам и скрыта, как и сами классы.

class FactoryA; class IA < public: IA(); virtual void foo() = 0 > class A: public virtual IA < public: A(FactoryA&); void foo() <>; > class WrapperA < public: WrapperA(FactoryA& factory) : m_IA(*new A(factory) < >~WrapperA() < delete &m_IA; > private: IA& m_IA; >

Re[8]: Использование ссылок в качестве полей класса

От: VladEC
Дата: 16.06.11 10:31
Оценка:

Здравствуйте, wander, Вы писали:

VEC>>>>>>А если исключения не поддерживаются компилятором или выключены?
W>>>>>А этот случай, именно такой?
VEC>>>>Да.
W>>>Я бы в таком случае все равно код писал так, чтобы если исключения в системе вдруг появились, то ничего не поломалось бы. Просто не использовать их в коде явно. А так ведь есть полно техник обеспечения безопасности исключений, которые не требует использования ключевых слов try-catch и throw.
VEC>>Э-э-э. Придется искать техники
VEC>>Я один только способ смог сходу придумать, озвучивать не буду
W>RAII.

Ну, я конструктор и захват системных ресурсов обычно стараюсь в разные функции вынести.
Чтобы мух от котлет отделить (выделение памяти от собственно общения с системой).

VEC>>А что же он возвращает.
VEC>>Если память выделить не удалось, а код компилируется без поддержки исключений.
VEC>>Как бы попробовать.
W>А ничего не возвращает. То, что ты отключил исключения в своем проекте не означает, что ты отключил их везде. Если мы говорим про ключик fno-exceptions, то исключение все равно будет генерироваться new. Только вот поймать его ты уже не сможешь.

W>Скомпилируй с ключиком -fno-exceptions
W>

W>#include W>#include W>int main() W> < W>char * p = new char[std::numeric_limitsunsigned long>::max()]; W> if(!p) W> < W>printf("not allocated\n"); W> > W> delete[] p; W>> W>

Действительно бросает исключение.
Не знал. Спасибо за наводку.
Это теперь надо заставить всех писать std::nothrow.
(или писать на Си и не выеживаться )

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *