6

すべての仮想関数をプライベートまたはプロテクトとして基底クラス インターフェイスを構築するのには十分な理由があります (これを参照してください)。しかし、派生クラス (外部クライアントの手に渡っている可能性があります) がプライベート仮想関数をパブリックとして作成するのをどのように防ぐのでしょうか? Virtually Yoursでは、著者はこの問題について語っていますが、解決策については議論されていません。

編集:あなたの答えから、私が以前に考えたように、これを防ぐ方法はないようです。しかし、この状況では失敗しやすい (クライアントが保護された仮想関数に確実にアクセスする) ため、コンパイラがそのような使用法について警告するのは理にかなっています。g++でテストしてみました。まず、次のように書きました。

class A {
        protected:
        virtual void none() { return; }
};

class B: public A {
        public:
        void none() { return; }
};

g++ -c -Wall -pedantic file.cppエラーなしでコンパイルされました。追加-Weffc++すると警告が表示されました: warning: ‘class A’ has virtual functions and accessible non-virtual destructor、これは理にかなっています。仮想デストラクタを追加した後、警告はありません。したがって、この間違いやすいケースには警告はありません。

4

6 に答える 6

13

Bjarne が述べたように、C++ のアクセス制御は、Machiavelli ではなく、Murphy から保護することを目的としています。一般的に同じことが言えます。その機能は事故から保護するためのものであり、意図的に間違ったことをする人ではありません.

C++ を使用するということは、ある程度、ソース コードにアクセスできる他の人々をある程度信頼することを意味します。彼らがどうしてもやりたければ、あらゆる方法で物事を台無しにすることができ、あなたは彼らを止めるために何もできません。コードの使用方法に実際の制限を加えたい場合、C++ は目的に適わない言語です。

編集: これは実際には「議論」ではありません。決定が下された根拠を単に指摘しているだけです。以前の質問に答えた D&E のコピーを持っているので、ここにある場合はもう少し入力します1 :

すべての誤用を防ぐよりも、便利な機能を許可することが重要です言語。機能の偶発的な誤用の可能性を最小限に抑えることが重要であり、C++ コンストラクトのデフォルトの動作が適切であるか、コンパイル時のエラーにつながるかを確認するために多くの努力が費やされてきました。たとえば、既定では、すべての関数引数の型がチェックされます (別々のコンパイル境界をまたいでいる場合でも)。既定では、すべてのクラス メンバーはプライベートです。ただし、システム プログラミング言語は、熱心なプログラマーがシステムを破壊するのを防ぐことはできないため、設計作業は、避けられない悪いプログラムを防ぐよりも、良いプログラムを作成するための機能を提供することに費やされます。長期的には、プログラマーは学習するようです。これは、古い C の「プログラマーを信頼する」というスローガンの変形です。さまざまな型チェックおよびアクセス制御規則が存在するため、クラス プロバイダーはユーザーに何を期待するかを明確に記述し、アクシデントから保護することができます。これらの規則は、意図的な違反に対する保護を意図したものではありません (§2.10)。

§2.10 で、彼はとりわけ次のように述べています。

保護システムのタスクは、そのような型システムの違反が明示的であることを確認し、そのような違反の必要性を最小限に抑えることです。

これらの目標はここで達成されたようです。保護された基本クラス メンバーを公開するには、派生クラスで明示的なアクションが必要であり、20 年以上 C++ を書いてきましたが、それを行う必要があった (または望んでいた) ことは思い出せません。

1 §4.3、ページ。115、116。

于 2010-01-27T01:36:09.410 に答える
2

C++ のアクセス制御では、意図したとおりに動作しない可能性があります。アクセスの共有を停止するために DRM スタイルの制約を適用することを意図したものではありません。A が B にアクセスできる場合、A は B を呼び出して、B にアクセスできない別の呼び出し元に返すなど、任意の目的で結果を使用できます。

リンク先の記事で説明されている問題は、A が故意または悪意を持って B を共有することではありません。公開されたインターフェイスにパブリック仮想関数を配置し、後でクラスを変更して、提案されたものを使用するようにするとどうなるかについてです。プライベート仮想関数を含むテンプレート メソッド パターン。子クラスは仮想関数のパブリック オーバーライドを記述しているため、すべての子クラスを変更しない限り、2 つの問題 (アクセスと仮想性) を分離することはできません。私が読んだ方法では、記事はそれが提示する問題の解決策を提供しており、その解決策は「そもそも仮想関数を公開しない」ことです。

仮想関数は、データ メンバーと同じように扱う必要があります。つまり、設計上のニーズにより制限の少ないアプローチが示されるまで、非公開にします。それらをよりプライベートなレベルに降格するよりも、よりアクセスしやすいレベルに昇格させる方がはるかに簡単です。

これで問題が解決しない理由は、彼らがあなたの問題を考慮していないからです。

于 2010-01-27T01:32:16.573 に答える
2

派生クラスでプライベート/保護された仮想メソッドをパブリックに昇格しても、基本クラスのメソッドは公開されません。基本クラスのポインターを介して呼び出すことはできません。基本クラスのインターフェースの一部にはなりません。

于 2010-01-27T01:50:08.693 に答える
2

コンパイラがそのような使用法について警告するのは理にかなっています。

なぜでしょうか?外部インターフェイスを決定するのは、各クラスの設計者次第です。

C++ では、基底クラスには、派生クラスのインターフェイス プロパティを強制する特別な機能はありません。派生クラスは、基本関数が public である場合に一部のオーバーライド関数を private にするか、またはその逆にすることを決定できます。派生クラスのインターフェイスは、基本クラスではなくクライアントとのコントラクトです (奇妙に繰り返されるテンプレート基本クラスのように、基本クラスが派生クラスのクライアントでない限り)。

于 2011-12-26T01:58:25.197 に答える
2

できません。関数の「仮想性」とアクセス タイプは、2 つの異なる無関係な概念です。

于 2010-01-27T01:19:38.867 に答える
1

派生型によってのみ構築可能なトークン引数が必要になる場合があります。もちろん、トークンのサブクラスを公開することもできます。したがって、仮想デストラクタを指定して、RTTI チェックを行う必要があります。

protected:
class ProtectedToken { virtual ~ProtectedToken() { } };
virtual void my_tough_cookie(int arg,
  ProtectedToken const &tok = ProtectedToken() ) {
    assert ( typeid( tok ) == typeid( ProtectedToken ) );
    …
}

もちろん、これはあなた自身を含め、誰にとっても良いことではありません。

編集:ああ、それは動作しません。public: using Base::ProtectedTokenあったとしても、その方法でプロテクションを破ることができます。私の人生のさらに 15 分間が無駄になりました…</p>

于 2010-01-27T02:31:54.603 に答える