2015-05-04 2 views
8

Я никогда не использовал какой-либо умный указатель, но я продолжаю читать о них почти везде, когда тема является указателем. Я понимаю, что есть ситуации, когда интеллектуальные указатели работают гораздо лучше, чем исходные указатели, потому что в какой-то степени они управляют владением указателем. Тем не менее, я до сих пор не знаю, где находится строка «Я не нуждаюсь в умных указателях для этого» и «это случай для интеллектуальных указателей».Почему я должен использовать интеллектуальную указатель в этой ситуации?

Позволяет сказать, у меня есть следующая ситуация:

class A { 
public: 
    double get1(){return 1;} 
    double get2(){return 2;} 
}; 
class SomeUtilityClass { 
public: 
    SomeUtilityClass(A* a) : a(a) {} 
    double getResult(){return a->get1() + a->get2();} 
    void setA(A* a){a = a;} 
private: 
    A* a; 
}; 
int main(int argc, char** argv) { 
    A a; 
    SomeUtilityClass u(&a); 
    std::cout << u.getResult() << std::endl; 
    A a2; 
    u.setA(&a2); 
    std::cout << u.getResult() << std::endl; 
    return 0; 
} 

Это, конечно, является упрощенным примером. Я имею в виду, что SomeUtilityClass не должен «владеть» экземпляром A (потому что это всего лишь класс утилиты), поэтому он просто удерживает указатель.

Что касается указателя, единственное, что я знаю, что может пойти не так, это:

  • SomeUtilityClass может быть реализован с нулевым указателем
  • Объект указал на может быть удален/выйти из сфера, без SomeUtilityClass заметил, что

Как мог умный указатель поможет избежать этой проблемы? Какие еще преимущества я получу, используя интеллектуальный указатель в этом случае?

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

+7

Исходные указатели прекрасно фиксируют концепцию несоблюдения ссылок на данные с ограниченным сроком службы. Здесь вы не должны использовать интеллектуальные указатели. – Mankarse

+5

Если вы используете 'new' в дикой природе, это довольно сильный намек, вам может понадобиться умный указатель. Если вы используете 'new []', это довольно сильный намек, вам может понадобиться 'std :: vector' или другой контейнер. – JBL

+1

Если 'nullptr' не имеет смысла, вы можете вместо этого использовать ссылку или' std :: reference_wrapper'. –

ответ

1

Для целей этого ответа я переосмысление узнающего как:

void setA(A* new_a){a = new_a;} 

Рассмотрим:

// Using your SomeUtilityClass 

int main() { 
    A a; 
    SomeUtilityClass u(&a); 
    // We define a new scope, just because: 
    { 
    A b; 
    u.setA(&b); 
    } 
    std::cout << u.getResult() << '\n'; 
    return 0; 
} 

После того, как сфера закончена, SomeUtilityClass имеет оборванный указатель и getResult() вызывает Неопределенным Поведение. Обратите внимание, что это невозможно решить с помощью ссылки: вы все равно получите свисающий.

Теперь рассмотрим вариант с использованием смарт-указатель:

class SomeUtilityClass { 
public: 
    SomeUtilityClass(std::shared_ptr<A>& a) : a{a} {} 
    double getResult(){return a->get1() + a->get2();} 
    void setA(std::shared_ptr<A>& new_a){a = new_a;} 
private: 
    std::shared_ptr<A> a; 
}; 

int main() { 
    std::shared_ptr<A> a{new A}; 
    SomeUtilityClass u{a}; 
    // We define a new scope, just because: 
    { 
    std::shared_ptr<A> b{new A}; 
    u.setA(b); 
    } 
    std::cout << u.getResult() << '\n'; 
    return 0; 
} 

Поскольку вы долевой собственности, не существует способа, чтобы получить оборванных указатель. Память, на которую указывает b, будет удалена, как обычно, но только после того, как u будет уничтожен (или его указатель будет изменен).

IMHO, в большинстве случаев вы должны использовать интеллектуальные указатели (даже если сначала это не имеет большого смысла). Это упрощает техническое обслуживание. Используйте исходные указатели только в конкретном коде, который им действительно нужен, и инкапсулируйте/изолируйте этот код как можно больше.

1

Если SomeUtilityClass не принадлежит переменная-член a, тогда умный указатель не имеет смысла.

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

2

Это зависит от того, как создается и сохраняется параметр. Если у вас нет памяти, и она может быть либо статически, либо динамически распределена, необработанный указатель - вполне разумное решение, особенно если вам необходимо поддерживать обмен данными как в вашем примере. Другим вариантом было бы использовать std::reference_wrapper, который избавился бы от вашего вопроса nullptr, сохраняя ту же семантику.

Если вы держите указатель на какой-либо общий ресурс (т. Е. Где-то в файле std::shared_ptr) и хотите проверить, было ли оно удалено или нет, вы можете провести std::weak_ptr.

1

По умолчанию способ выражения указателя, не являющегося владельцем, на C++ равен weak_ptr.Для использования weak_ptr вам нужно использовать shared_ptr для собственности, поэтому в вашем примере вы будете использовать

shared_ptr<A> owner(...) 

вместо

A a 

Тогда как частный член указателя вашего SomeUtilityClass использовать слабый указатель:

weak_ptr<A> w; 

и инициализировать его shared_ptr:

SomeUtilityClass(shared_ptr<A> o) : w(o) {} 

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

shared_ptr<A> locked = w.lock(); 

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

Оба shared_ptr и weak_ptr доступны в стандартной библиотеке в C++ 11 и в Boost для старых компиляторов.

+1

'weak_ptr' не является способом по умолчанию для выражения прав собственности.Он просто используется для прерывания циклов, когда используется 'shared_ptr'. По умолчанию метод выражения non-owning является необработанным указателем. –

+0

@SebastianRedl Вы изложили свое личное мнение, как принято считать. Тем не менее, [cppreference] (http://en.cppreference.com/w/cpp/memory/weak_ptr) говорит, что временное владение _std :: weak_ptr моделирует: когда объект должен быть доступен, только если он существует_ и позже _ Кроме того, std :: weak_ptr используется для разрыва круговых ссылок, поэтому по крайней мере там - и во многих других местах - высказываются разные мнения. –

+0

И я использовал свою личную репутацию, чтобы снизить ваш ответ. Вот как работает SO. Кроме того, как вы читаете «моделирование временного владения» в качестве поддержки вашего заявленного пункта «метод по умолчанию для выражения не имеющего указателя»? Во всяком случае, cppreference * противоречит * вашей записи и * поддерживает * мой комментарий. –

0

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

SomeUtilityClass может быть реализован с нулевым указателем

Это один, вероятно, лучше всего решается с проверкой в ​​конструкторе, выбрасывая исключение или указывающий на ошибку каким-либо иным образом, в том случае, когда вы получаете указатель NULL в качестве аргумента. Я не могу себе представить, как поможет умный указатель, если вы не используете специальный класс интеллектуальных указателей, который не принимает NULL, поэтому он уже проверяет вас.

Объект указал, могут быть удалены/выходят из области видимости, без SomeUtilityClass заметил, что

Это одна на самом деле может быть решена с помощью специального типа смарт-указатели, но это необходимо что объект, на который указывает, каким-то образом поддерживает уведомление об уничтожении. Одним из таких примеров является класс QPointer в библиотеке Qt, который может указывать только на QObject экземпляры, которые уведомляют об этом при удалении, поэтому умный указатель автоматически становится NULL, когда объект удаляется. Однако есть некоторые проблемы с этим подходом:

  1. Нужно проверять NULL каждый раз, когда вы получаете доступ к объекту с помощью умного указателя.
  2. Если умные указатель указывает на экземпляр класса, скажем MyClass, расширения Класс выполнения уведомления удаления (QObject в случае Qt), вы получите странные результаты: это деструктор QObject, который уведомляет смарт-указатель , поэтому возможно, что вы получите к нему доступ, когда деструктор MyClass уже начал свою грязную работу, поэтому объект частично разрушен, но указатель не является NULL еще, поскольку уничтожение все еще продолжается.
+1

Теперь я обычно игнорирую downvotes, но в этом случае я четко ответил на точки, обозначенные OP, предоставив пример из широко используемой библиотеки C++. Что на Земле так неправильно с моим ответом, что кто-то пожертвовал целой точкой репутации, чтобы просто рассказать миру, насколько он плох и опасен? Может быть, ты скажешь мне, и я это исправим? –

Смежные вопросы