48

std::observer_ptrライブラリ基本技術仕様 V2の構造のポイントは正確には何ですか?

T*動的メモリの安全性を追加しない場合、これは余分なステップのように思えます。

すべてのコードstd::unique_ptrで、オブジェクトの明示的な所有権を取得する必要がある場所と、オブジェクトのstd::shared_ptr所有権を共有できる場所を使用しています。

これは非常にうまく機能し、すでに破棄されたオブジェクトの偶発的な逆参照を防ぎます。

std::observer_ptrもちろん、観測されたオブジェクトの寿命については保証しません。

std::unique_ptrまたはから構築されたstd::shared_ptr場合、そのような構造での使用が見られますが、単純に使用しているコードT*はおそらくそのまま使用し続け、何かに移行する予定がある場合は、std::shared_ptrおよび/またはstd::unique_ptr(使用中)。


簡単な関数の例を考えると:

template<typename T>
auto func(std::observer_ptr<T> ptr){}

監視中にスマート ポインターが格納されたオブジェクトを破棄するのを停止すると便利です。

しかし、私が観察したい場合、std::shared_ptrまたはstd::unique_ptr私は書く必要があります:

auto main() -> int{
    auto uptr = std::make_unique<int>(5);
    auto sptr = std::make_shared<int>(6);
    func(uptr.get());
    func(sptr.get());
}

これは、以下よりも安全ではありません。

template<typename T>
auto func(T *ptr){}

では、この新しい構造の用途は何ですか?

自己文書化ソースのためだけですか?

4

8 に答える 8

39

この提案は、それが自己文書化のためのものであることを明確に示しています。

このホワイト ペーパーではobserver_ptr、(それほどではない) スマート ポインター型を提案します。これは、そのポイント先、つまり監視対象のオブジェクトに対して所有権の責任を負わないものです。そのため、未加工のポインター型のほぼドロップインの代替として意図されており、語彙型として、コード リーダーによる詳細な分析を必要とせずに意図された用途を示すという利点があります。

于 2015-08-06T17:53:10.720 に答える
35

所有権の共有ではなく共有アクセスが必要な場合。

問題は、未加工のポインターがまだ非常に有用であり、完全に立派なユース ケース シナリオがあることです。

生のポインターがスマート ポインターによって管理される場合、そのクリーンアップが保証されるため、スマート ポインターの有効期間内にスマートポインターが管理している生のポインターを介して実際のデータにアクセスすることは理にかなっています。

したがって、通常は生のポインターを受け取る関数を作成する場合、関数がそのポインターを削除しないことを約束する良い方法は、 のような厳密に型指定されたクラスを使用することstd::observer_ptrです。

マネージ生ポインタを引数としてstd::observer_ptr関数パラメータに渡すと、関数がそのポインタを渡さないことがわかりますdelete

これは、関数が「ポインタをください。その割り当てには干渉しません。観察するために使用します」と言う方法です。

std::observer_ptrちなみに、見ることはできるが触れることはできないという意味なので、私はその名前に興味がありません。しかし、それは真実ではありません。もっと似たものを使っていたでしょうaccess_ptr

追記:

これは とは異なる使用例std::shared_ptrです。これは所有権std::shared_ptrの共有に関するものであり、どの所有オブジェクトが最初にスコープ外になるかを判断できない場合にのみ使用する必要があります。

一方、 は、アクセスstd::observer_ptrを共有したいが所有権を共有したくない場合に使用します。

std::shared_ptr単純にアクセスを共有するために使用するのは、非常に非効率になる可能性があるため、実際には適切ではありません。

そのため、 aまたは aを使用してターゲット ポインターを管理しているかどうかに関係なく、生ポインターのユースケースがあり、したがって a の合理性があります。std::unique_ptrstd::shared_ptrstd::observer_ptr

于 2015-08-06T18:11:56.207 に答える
23

ソースの自己文書化のためだけですか?

はい。

于 2015-08-06T17:44:21.710 に答える
6

over raw ポインターを使用することの良い結果の 1 つstd::observer_ptrは、C から継承された、混乱しやすくエラーが発生しやすい複数ポインターのインスタンス化構文よりも優れた代替手段を提供することです。

std::observer_ptr<int> a, b, c;

の改善です

int *a, *b, *c;

これは C++ の観点からは少し奇妙で、次のように簡単にタイプミスする可能性があります。

int* a, b, c;
于 2015-08-06T18:49:06.817 に答える
2

はい、ポイントstd::observer_ptrは主に「自己文書化」であり、それ自体が有効な目的です。しかし、「オブザーバー」ポインターが何であるかが正確には明らかではないため、おそらくそれはうまく機能しないことを指摘する必要があります。まず、Galik が指摘するように、一部の人にとっては、名前はターゲットを変更しないというコミットメントを暗示しているように見えますが、これは意図ではありませんaccess_ptr。そして第二に、修飾子がなければ、名前はその「非機能的」動作の承認を暗示します. たとえば、std::weak_ptrを「オブザーバー」ポインターのタイプと見なすことができます。ただしstd::weak_ptr、(割り当て解除された) オブジェクトへのアクセス試行を安全に失敗させるメカニズムを提供することにより、ポインターがターゲット オブジェクトよりも長く存続する場合に対応します。std::observer_ptrの実装はこのケースに対応していません。raw_access_ptrしたがって、機能上の欠点をより適切に示すため、おそらくより適切な名前になります。

では、当然のことながら、この機能的に問題のある「非所有」ポインターのポイントは何ですか? 主な理由はおそらくパフォーマンスです。多くの C++ プログラマーは、 のオーバーヘッドがstd::share_ptr高すぎると認識しているため、「オブザーバー」ポインターが必要な場合はそのまま生ポインターを使用します。提案されたstd::observer_ptr試みは、許容できるパフォーマンス コストでコードの明瞭さをわずかに改善することを目的としています。具体的には、パフォーマンス コストはゼロです。

残念ながら、生のポインターを「オブザーバー」ポインターとして使用することがどれだけ安全かについて、非現実的な楽観論が広まっているようですが、私の意見ではそうです。特に、ターゲット オブジェクトが より長く存続しなければならないという要件を述べるのは簡単ですがstd::observer_ptr、それが満たされていることを完全に確信することは必ずしも容易ではありません。次の例を検討してください。

struct employee_t {
    employee_t(const std::string& first_name, const std::string& last_name) : m_first_name(first_name), m_last_name(last_name) {}
    std::string m_first_name;
    std::string m_last_name;
};

void replace_last_employee_with(const std::observer_ptr<employee_t> p_new_employee, std::list<employee_t>& employee_list) {
    if (1 <= employee_list.size()) {
        employee_list.pop_back();
    }
    employee_list.push_back(*p_new_employee);
}

void main(int argc, char* argv[]) {
    std::list<employee_t> current_employee_list;
    current_employee_list.push_back(employee_t("Julie", "Jones"));
    current_employee_list.push_back(employee_t("John", "Smith"));

    std::observer_ptr<employee_t> p_person_who_convinces_boss_to_rehire_him(&(current_employee_list.back()));
    replace_last_employee_with(p_person_who_convinces_boss_to_rehire_him, current_employee_list);
}

関数の作成者はreplace_last_employee_with()、新規採用者への参照が、置き換えられる既存の従業員への参照である可能性があることに思いもよらなかった可能性があります。この場合、関数がstd::observer_ptr<employee_t>終了する前に、パラメーターのターゲットが誤って割り当て解除される可能性があります。それを使用しています。

これは不自然な例ですが、この種のことは、より複雑な状況で簡単に発生する可能性があります。もちろん、ほとんどの場合、生のポインターを使用しても完全に安全です。問題は、実際には安全ではないのに、安全であると簡単に思い込んでしまう少数のケースがあることです。

std::observer_ptr<employee_t>パラメータをstd::shared_ptrorに置き換えることstd::weak_ptrが何らかの理由で受け入れられない場合、別の安全なオプションがあります. 「登録済みポインター」はnull_ptr、ターゲット オブジェクトが破棄されたときに (自動的に) 設定されることを除いて、生のポインターと同じように動作するスマート ポインターであり、既に削除されているオブジェクトにアクセスしようとすると、既定で例外がスローされます。 . それらは一般的に高速ですstd::shared_ptrs よりも優れていますが、パフォーマンスの要求が非常に厳しい場合は、登録されたポインターをコンパイル時のディレクティブで "無効" (対応する生のポインターに自動的に置き換える) ことができ、デバッグで使用できる (そしてオーバーヘッドが発生する) ことができます。 /test/beta モードのみ。

したがって、生のポインターに基づく「オブザーバー」ポインターが存在する場合は、おそらく、登録されたポインターに基づくポインターが存在し、おそらく OP が示唆したように、std::shared_ptr に基づくポインターも存在するはずです。

于 2016-02-19T15:08:29.760 に答える