#c #reference
#c #ссылка
Вопрос:
Я работаю над классом, в котором я пытаюсь использовать постоянные ссылочные члены, которые ссылаются на другие переменные-члены, чтобы обеспечить общедоступный постоянный доступ к этим членам. Есть ли законный способ сделать это?
Код компилируется, но выдает мне предупреждения о привязке ссылочного элемента к временным значениям. После resize()
вызова A
каким-то образом все еще указывает на null, хотя myA
обновляется.
Ниже приводится воссоздание того, что я пытаюсь сделать. Я думаю, что бит самоссылки является незаконным и приводит к проблеме, но я не уверен. Любой ввод будет высоко оценен.
class MyClass{
private:
int mySize;
double * myA;
void resize(int inSize) {
mySize = inSize;
delete [] myA;
// detection for non-positive size is not included for brevity
myA = new double [size];
}
public:
const int amp; size;
const double * const amp; A;
MyClass():
mySize(0) , myA(nullptr)
size(mySize), A(myA) {}
// dtor is not included
};
int main(){
MyClass myObj;
myObj.resize(42);
// myObj.A[1] = 0.5 // seg fault
}
РЕДАКТИРОВАТЬ: Майкл указал, что эта проблема связана с const
определителями A
, и я подтвердил это, изменив определители A
, со следующим результатом:
double * amp;A
-> A
привязывается myA
правильно.
double * const amp;A
-> A
привязывается myA
правильно.
const double * amp;A
-> Несовместимо. Ошибка компилятора.
const double * const amp;A
-> A
привязывается к временной переменной.
РЕДАКТИРОВАТЬ: кажется, что лучшей практикой является использование функции получения.
Комментарии:
1. Где
size
установлено? Я вижуmySize = inSize
, ноsize =
ничего. Даже если это законно, это довольно странная конструкция.2. И почему у вас есть общедоступные ссылки на ваших собственных членов?
3. Должно сработать, но поскольку ссылки не могут быть повторно установлены, вы теряете возможность назначать класс (кстати, ознакомьтесь с Правилом трех и друзьями). Почему не
const
возвращать функции получения? Они будут скомпилированы ни к чему. Кроме того, рассмотрите возможность использованияstd::vector
. Он обрабатывает все управление памятью для вас (в том числе соответствует правилу пяти, поэтомуMyClass
может придерживаться правила нуля).4. @user4581301 Я знаю о функциях получения, просто интересно, сработает ли этот альтернативный подход. Я не думаю, что эта ссылка помешает копированию / присвоению, потому что класс может просто изменить значение
mySize
/myA
… ?5. @0x499602D2 В основном в качестве альтернативы для
const double * getA() const;
. Функции получения хороши и все такое, но, как я полагаю, выглядят довольно громоздко в коде.
Ответ №1:
Проблема с инициализацией A(myA)
заключается в том, что A
имеет тип const double * const amp;
, а myA
имеет тип double *
. Тип, на который A
ссылается, не совместим со ссылкой на тип myA
(спасибо @guest за разъяснение этого). В результате ссылка A
будет привязана к временному объекту, материализованному из prvalue, который является результатом неявного преобразования myA
в const double * const
[dcl.init.ref]/5.4.2. Однако время жизни временного элемента не будет продлено [class.temporary]/5, и, таким образом, временное значение будетуничтожается сразу после инициализации, A
оставляя зависание. Это то, о чем компилятор предупреждает вас здесь.
Нет ничего противозаконного в том, чтобы иметь ссылки на ваших собственных членов. Вопрос в том, какой смысл это делать? Хотя ссылки сами по себе не являются объектами, и не указано, занимают ли они хранилище или нет [dcl.ref]/ 4, компиляторы обычно реализуют ссылочные переменные-члены, вводя поля, содержащие адреса объектов, на которые ссылается ссылка (другими словами: ссылки будут компилироваться в основном до указателей). В результате половина памяти, используемой объектом типа MyClass
в вашем примере выше, будет использоваться для хранения адресов, которые указывают на объект, в котором хранятся сами адреса. Знание того, где найти один из этих адресов, требует уже знания того, на что они указывают…Так что это не незаконно, но, вероятно, это не такая уж хорошая вещь…
Комментарии:
1. Спасибо, что нашли эти цитаты. Сэкономил мне кучу времени.
2. Тогда в GCC 8 была такая же или очень похожая ошибка. GCC выдал предупреждение, но скомпилировался, и программа напечатала неправильный адрес для ссылки.
3. @MichaelKenzel Спасибо за ответ! Протестировано на gcc5.4, кажется, отлично компилируется.
4. @YifanLai обратите внимание, что это может быть неправильно, мне придется проверить более тщательно, обновлю, как только узнаю больше…
5. Проблема
const double *
в том , что да. Только квалификаторы верхнего уровня могут отличаться для прямой привязки.
Ответ №2:
Типы double*
и const double*
несовместимы, поэтому вы не можете инициализировать ссылку на последний из экземпляра первого. Однако это не является незаконным, но вместо этого компилятор генерирует временную привязку к ссылке (что приводит к зависшей ссылке — ИМХО, это не должно быть разрешено, т. Е. Быть Ошибкой).
Если вы намеревались предоставить public
const
доступ к своим private
данным, то стандартным методом является использование функций-членов:
int size() const noexcept { return mySize; }
const double* A() const noexcept { return myA; }
который будет оптимизирован компилятором.
Еще один способ заставить ваш код работать — добавить еще один закрытый элемент:
double*myA;
const double*myAconst;
const double* constamp; A = myAconst;
и убедитесь, что myA
и myAconst
всегда указывают на один и тот же адрес. Вы также можете избегать myA
в пользу myAconst
и использовать const_cast<double*>(myAconst)
, если вам нужно изменить выделенные элементы (это законное использование const_cast<>
, поскольку адрес указывает на подлинные непостоянные данные.