Геттер для больших переменных-членов без копирования - PullRequest
2 голосов
/ 01 мая 2020

У меня есть класс, содержащий большие переменные-члены. В моем случае, большая переменная-член является контейнером многих объектов, и она должна быть закрытой, поскольку я не хочу позволять пользователю изменять ее напрямую

class Example {
 public:
  std::vector<BigObject> get_very_big_object() const { return very_big_object; }
 private:
  std::vector<BigObject> very_big_object;
}

Я хочу, чтобы пользователь мог просмотреть объект без копирования:

Example e();
auto very_big_object = e.get_very_big_object();  // Uh oh, made a copy
cout << very_big_object[11];  // Look at any element in the vector etc

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

const std::vector<BigObject>& get_very_big_object() const { return very_big_object; }

Я прочитал эту статью , которая предполагает, что это может быть рискованно, и что умный указатель std::unique_ptr может быть лучше, но эту проблему лучше всего решить с помощью современной семантики перемещения C ++ 11. Но я обнаружил, что немного крипти c.

Какая современная лучшая практика для этого?

Ответы [ 2 ]

3 голосов
/ 01 мая 2020

Я прочитал эту статью , которая предполагает, что это может быть рискованно, и что умный указатель std::unique_ptr может быть лучше, но эту проблему лучше всего решить с помощью современного C ++ 11 хода. семантика.

С этой точки зрения статья совершенно неправильная. Интеллектуальный указатель не устраняет «риск».

Краткий обзор соответствующих частей статьи

  • Если класс возвращает ссылку const на элемент данных, клиентский код может вводить const_cast и, таким образом, изменять элемент данных, не проходя через API класса.
  • В статье предлагается (неправильно) , что вышеупомянутого можно избежать с помощью умного указателя. Настройка заключается в том, чтобы класс поддерживал общий указатель на данные и чтобы получатель возвращал этот указатель на приведенный общий указатель на const данные.

Критика точек

Прежде всего, это не работает. Все, что нужно сделать, это отменить ссылку на интеллектуальный указатель, чтобы получить const ссылку на данные, которая может быть тогда const_cast, как и раньше. Используя собственный пример автора, вместо

std::string &evil = const_cast<std::string&>(obj.someStr());

используйте

std::string &evil = const_cast<std::string&>(*obj.str_ptr());

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

Во-вторых, это не ваша забота. Когда вы возвращаете ссылку const, вы сообщаете клиентскому коду, что это значение не должно изменяться. Если клиентский код делает так или иначе, это клиентский код нарушил соглашение. По сути, клиентский код вызывал неопределенное поведение, поэтому ваш класс может делать что угодно, даже взломать sh программу.

Каков современный лучший метод для этого?

Просто верните const ссылку. (Большинство правил имеют исключения, но, по моему опыту, это правило выполняется в 95-99,9% случаев.)

0 голосов
/ 01 мая 2020

Что я делал, когда работал над своей BDD -библиотекой для школы, - это создавал класс-оболочку VeryBigObject, который связывается с одноэлементным экземпляром при создании экземпляра и скрывает указатель подсчета ссылок, оттуда вы можете переопределить operator->() метод, обеспечивающий прямой доступ к методам класса.

Итак, что-то вроде этого

class VeryBigObject {
private:
  vector<BigObject>* obj;
public:
  VeryBigObject() {
    // find a way to instantiate with a pointer, not by copying
  }
  VeryBigObject(const VeryBigObject& o) {
    // Update reference counts
    obj = o.obj;
  }
  virtual VeryBigObject operator->(const VeryBigObject&); // I don't remember how to do this one, just google it.
  ... // Do other overloads as you see fit to mask working with the pointer directly.
};

Это позволяет вам создать небольшой портативный класс, о котором вам не нужно беспокоиться о копировании, но также имеет доступ к большему объекту легко. Вам все равно придется беспокоиться о таких вещах, как кэширование и тому подобное, хотя

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...