Більшість відповідей тут не стосуються притаманної неоднозначності щодо того, щоб мати необроблений покажчик у підписі функції, з точки зору вираження намірів. Проблеми такі:
Абонент не знає, чи вказує вказівник на окремі об'єкти, або на початок "масиву" об'єктів.
Абонент не знає, чи "вказівник" володіє пам'яттю, на яку він вказує. IE, чи повинна функція звільнити пам'ять. ( foo(new int)
- Це витік пам'яті?).
Абонент не знає, чи nullptr
можна безпечно перейти до функції.
Усі ці проблеми вирішуються посиланнями:
Посилання завжди стосуються одного об'єкта.
Посилання ніколи не володіють пам’яттю, на яку вони посилаються, вони є лише переглядом пам’яті.
Посилання не можуть бути нульовими.
Це робить посилання набагато кращим кандидатом для загального використання. Однак посилання не є ідеальними - є кілька основних проблем, які слід врахувати.
- Відсутня явна непрямість. Це не є проблемою із необробленим покажчиком, оскільки ми повинні використовувати
&
оператор, щоб показати, що ми дійсно передаємо вказівник. Наприклад, int a = 5; foo(a);
тут зовсім не ясно, що a передається через посилання і може бути змінено.
- Зменшення. Ця слабкість покажчиків також може бути сильною, коли ми насправді хочемо, щоб наші посилання були нульовими. Вважаючи, що
std::optional<T&>
це не дійсно (з поважних причин), вказівники дають нам ту нульовість, яку ви хочете.
Тож здається, що коли ми хочемо нульової посилання з явним непрямим впливом, ми повинні досягти T*
права? Неправильно!
Абстракції
У нашому відчаї до мінливості ми можемо досягти T*
і просто ігнорувати всі перелічені раніше недоліки та смислові неоднозначності. Натомість нам слід досягти того, що C ++ найкраще робить: абстракція. Якщо ми просто пишемо клас, який обертається навколо вказівника, ми набуваємо виразності, а також зворотності та явної непрямості.
template <typename T>
struct optional_ref {
optional_ref() : ptr(nullptr) {}
optional_ref(T* t) : ptr(t) {}
optional_ref(std::nullptr_t) : ptr(nullptr) {}
T& get() const {
return *ptr;
}
explicit operator bool() const {
return bool(ptr);
}
private:
T* ptr;
};
Це найпростіший інтерфейс, який я міг би придумати, але він робить свою роботу ефективно. Це дозволяє ініціалізувати посилання, перевіряти, чи існує якесь значення, і отримувати доступ до значення. Ми можемо використовувати його так:
void foo(optional_ref<int> x) {
if (x) {
auto y = x.get();
// use y here
}
}
int x = 5;
foo(&x); // explicit indirection here
foo(nullptr); // nullability
Ми досягли своїх цілей! Давайте тепер подивимося на переваги, порівняно із сирим покажчиком.
- Інтерфейс чітко показує, що посилання має стосуватися лише одного об’єкта.
- Очевидно, що вона не володіє пам'яттю, на яку посилається, оскільки не має визначеного користувачем деструктора і не має способу видалення пам'яті.
- Користувач знає, що
nullptr
може бути переданий, оскільки автор функції явно запитує анoptional_ref
Звідси ми можемо зробити інтерфейс більш складним, наприклад, додавання операторів рівності, монадики get_or
та map
інтерфейсу, методу, який отримує значення або кидає виняток, constexpr
підтримку. Це ти можеш зробити.
На закінчення, замість того, щоб використовувати необроблені покажчики, поясніть, що саме ці вказівники насправді означають у вашому коді, або використовуйте стандартну абстракцію бібліотеки або пишіть свою власну. Це значно покращить ваш код.
new
створити покажчик і виникаючі проблеми власності.