Разрешено ли изменять член данных const вне класса? - PullRequest
3 голосов
/ 25 марта 2020

Если у класса есть элемент справочных данных const, который может меняться вне области действия такого класса, является ли это неопределенным поведением?

В качестве примера давайте рассмотрим следующий код C ++:

#include <iostream>

class A {
  int x;
public:
  A(int x): x(x){}
  void change(int y){
    x = y;
  }
  friend std::ostream & operator << (std::ostream & os, const A & a){
    os << a.x;
    return os;
  }
};

class B {
  const A & a;
public:
  B(const A & a) : a(a) {}
  friend std::ostream & operator << (std::ostream & os, const B & b){
    os << b.a;
    return os;
  }
};

int main(){
  A a(1);
  B b(a);
  std::cout << a << std::endl;
  std::cout << b << std::endl;
  a.change(2);
  std::cout << a << std::endl;
  std::cout << b << std::endl;
}

Мой компилятор смог выполнить его правильно, и отладчик указал, что x B :: a был изменен.

Спасибо за вашу помощь!

Ответы [ 2 ]

4 голосов
/ 25 марта 2020

Вы не можете изменить объект, используя постоянную ссылку на него, но вы можете изменить сам объект, если он не является константным или использует непостоянную ссылку на объект.

Рассмотрим следующую демонстрационную программу.

#include <iostream>

int main() 
{
    int x = 10;

    int &rx = x;
    const int &crx = x;

    std::cout << "rx = " << rx << '\n';
    std::cout << "crx = " << crx << '\n';

    rx = 20;

    std::cout << "rx = " << rx << '\n';
    std::cout << "crx = " << crx << '\n';

    return 0;
}

Его вывод

rx = 10
crx = 10
rx = 20
crx = 20

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

#include <iostream>

int main() 
{
    int x = 10;

    int *px = &x;
    const int *cpx = &x;

    std::cout << "*px = " << *px << '\n';
    std::cout << "*cpx = " << *cpx << '\n';

    *px = 20;

    std::cout << "*px = " << *px << '\n';
    std::cout << "*cpx = " << *cpx << '\n';

    return 0;
}
4 голосов
/ 25 марта 2020

Это не неопределенное поведение. Ссылка const, являющаяся членом B, означает только то, что экземпляр B не может изменить его через эту ссылку. Однако, поскольку это ссылка, что-то еще может изменить ее - включая других членов B, которые имеют свою собственную не-const ссылку на тот же экземпляр A.

Сравните дополнение члена c к существующему классу B, и обратите внимание, что мы успешно меняем его в пределах B::changeA() через ссылку, отличную от const, а также с C::change() в main():

#include <iostream>

class A {
  int x;
public:
  A(int x): x(x){}
  void change(int y){
    x = y;
  }
  friend std::ostream & operator << (std::ostream & os, const A & a){
    os << a.x;
    return os;
  }
};

class C
{
    A& a;
public:
    C(A& a) : a{a} {}
    void change(int y) { a.change(y); }
};

class B {
  const A & a;
  C& c;
public:
  B(const A & a, C& c) : a(a), c{c} {}
  friend std::ostream & operator << (std::ostream & os, const B & b){
    os << b.a;
    return os;
  }
  void changeA(int y) { c.change(y); }
};

int main(){
  A a(1);
  C c(a);
  B b(a,c);

  std::cout << a << ' ' << b << '\n';

  a.change(2);
  std::cout << a << ' ' << b << '\n';

  b.changeA(3);
  std::cout << a << ' ' << b << '\n';

  c.change(4);
  std::cout << a << ' ' << b << '\n';
}

Посмотри, как он работает на Coliru , который печатает:

1 1
2 2
3 3
4 4
...