質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

8199閲覧

c++protectedの動作がいまいちわからないです

mmmisaki

総合スコア34

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/02/07 16:31

編集2019/02/07 16:41

以下コードでコンパイラが嫌がる理由嫌がるようにした理由が分からないです

以下のようなコードを書いてコンパイルしたのですが、
protected within this context
というエラーが発生してコンパイルできません

Baseクラスにfriendをつければコンパイルは可能なのですが、
Baseクラスは抽象クラスですし、
実際に使用される場合ExtendクラスのBaseポインタには派生クラスであるAクラスのポインタが格納されるのでfriendつけるのは派生先だけで十分であるように思います。
基本クラスがまだ普通のクラスならばコンパイラが怒るのも納得できるのですが、
抽象クラスだったら継承クラスすべてでfriend宣言されていれば、別に怒る必要はないと思うのですが、コンパイラは何がそんなにいやなんでしょうか?(多分昔のc++コンパイラなら許してるのでは?)

#include <iostream> using namespace std; class Base { //friend class Extend; //コメントアウト切ったらエラーは消える protected : virtual int print() const = 0; protected : virtual ~Base() {} }; class A : public Base { friend class Extend; public : A(int k) : n(k) {} private : int n; int print() const { return n; } }; class Extend { public : Extend(int); public : ~Extend() { delete p; }; //deleteしたいがprotectecなので怒る private : Base* p; }; Extend::Extend(int n) { p = new A(n); } int main(void) { }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

Baseポインタには派生クラスであるAクラスのポインタが格納されるので

これを理解しているのはプログラマーだけです。

ExtendからみればpはBaseとしてしか保持しないので、たとえ自身のメソッド内でpにAのインスタンスを代入したとしてもBaseとしてしか扱いません。
pをAとして扱えるのはプログラマーによって明示的にキャストされた場合のみです。

delete p のタイミングではそれがvirtualであろうと、Extendから見れば呼び出すものはBase::~Baseなので、それはprotectedだからアクセスできないのです。

そうしないと、mmmisakiさんが思っているような例を許可した時、追加でB/C/DがそれぞれBaseから派生していて、A/BはBaseのfriendだけどC/Dはfriendでない場合、delete pの文脈は呼び出していいのかダメなのか判断できなくなります。
そのため、昔のコンパイラができたか、と言われたらそんなことは無いはずです。

投稿2019/02/07 17:05

toki_td

総合スコア2850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

抽象クラスだったら継承クラスすべてでfriend宣言されていれば、別に怒る必要はないと思うのですが、コンパイラは何がそんなにいやなんでしょうか?(多分昔のc++コンパイラなら許してるのでは?)

そのとおり、"すべてで"friendが指定されていれば問題ないでしょう
しかし、その全てで宣言されているかが判明するのはコンパイル時ではなくリンク時です
コンパイル時は1ファイル(とincludeファイル)しかみないので、
その他のファイルに何が書いてあるかはチェックしません

ですから、リンカーに「すべてかどうか」を申し送りする必要があるんですが、
リンカーの仕事はリンクすることで、構文チェックは仕事ではありません

コンパイルの段階でプロジェクトすべてのファイルを横断的に眺めてコンパイルできないの?と思います
実際、C#なんかはそんな感じですし、実現すればヘッダファイルも要らなくなるんですが、
そのへんは古い設計思想を引きずっているC++の弱点でもあります

投稿2019/02/08 03:11

izmktr

総合スコア2856

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mmmisaki

2019/02/09 17:42

個人的にはこちらの回答が納得できたのでベストアンサーにさせていただきました! ご回答ありがとうございました。
guest

0

こんにちは。

protectedは派生先のクラスなら常にアクセスできるというアクセス指定子ではありません。
規格書の該当ページ(n3337ならp239の11.4 Protected member access)にある例から推測すると以下のようです。

C++

1Extend::Extend() 2{ 3 print(); // OK 4 p->print(); // ill-formed 5}

派生先のクラス全部ではなく、派生先の同じインスタンスのみに限定されているようです。
protectedの主旨的に妥当な定義と感じます。

派生クラスではなく、同じクラスの場合は別インスタンスのprivateやprotectedメンバにもアクセスできます。でないとコピー・コンストラクタさえ書けなくなりますしね。

実際に使用される場合ExtendクラスのBaseポインタには派生クラスであるAクラスのポインタが格納されるのでfriendつけるのは派生先だけで十分であるように思います。

基本クラスがまだ普通のクラスならばコンパイラが怒るのも納得できるのですが、
抽象クラスだったら継承クラスすべてでfriend宣言されていれば、別に怒る必要はないと思う

確かにその通りですが、それをコンパイラの仕様とするには複雑すぎるように感じます。
そして、その仕様を採用することで便利になる場面が多数あるようにも思えません。
同様に感じる人が仕様を策定する人たちの中で多数派だったのかも知れません。

投稿2019/02/07 18:04

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問