問題
コードを少しずつ見てみましょう。
#include <iostream>
#include <string>
using namespace std;
ここで簡単に注意してください: を使用するのは悪い考えusing namespace std;
です。
class C
{
private:
string str;
friend void func();
};
ここで、クラス C を定義します。このクラスのオブジェクトにはプライベートな文字列が含まれることを宣言し (つまり、クラス メンバーとフレンドのみがアクセスできます)、グローバル関数void func()
をフレンドとして宣言します。つまり、許可されます。str
クラスC
およびタイプ の任意のオブジェクトのプライベート メンバー (この場合は ) にアクセスしますC
。そのパーミッションとは別にfunc
、クラスとはまったく関係がないことに注意してくださいC
。
void func()
{
str = "Lala";
cout << str << endl;
}
str
ここでは、宣言したことのない変数に代入しようとしています。タイプ のプライベート メンバーとオブジェクトにアクセスできること以外
func
に、クラスとの関係はないことに注意してください。ただし、タイプのオブジェクトは見えません。あったとしても、どのオブジェクトから取得するか、またはinについて話していることをコンパイラに伝えるものは何もありません。I'll remember you againは から完全に独立しているため、コードはがフレンドであると宣言しなかった場合と同じように解釈されます。C
C
C
C
str
str
C
func
C
C
int main()
{
func();
}
わかりました、ここでは特別なことは何もありませんfunc
。
修正方法
では、コードを修正するにはどうすればよいでしょうか。まあ、いくつかの可能性があります:
オブジェクトの提供
ローカル オブジェクト
は classのオブジェクトstr
のメンバーであるため、クラスのオブジェクトが必要になります。したがって、たとえば次のことができます。C
void func()
{
C object;
object.str = "Lala";
std::cout << object.str << std::endl;
}
ここでは、 でローカル オブジェクトを作成し、そのオブジェクトのメンバーに値をfunc
割り当ててから出力します。異なるオブジェクトが異なるメンバーを持っていることを確認するには、たとえば次のように記述できます。 str
void func()
{
C object1, object2;
object1.str = "Lala";
object2.str = "Lele";
std::cout << object1.str << " -- " << object2.str << "\n";
}
Lala -- Lele
これは、最初のオブジェクトのstr
メンバーが value"Lala"
を持ち、2 番目のオブジェクトのstr
メンバーがvalue を持っているため、出力されます"Lele"
。
関数の引数
別のオプションは、オブジェクトを引数として渡すことです。
void func(C object)
{
std::cout << object.str << " -- ";
object.str = "Lele";
std::cout << object.str << " -- ";
}
int main()
{
C main_object;
main_object.str = "Lala";
func(main_object);
std::cout << object.str << std::endl;
}
これは印刷されLala -- Lele -- Lala
ます。
ここで何が起こるかというと、メンバーに値が割り当てられmain
たオブジェクトが作成されます。を呼び出すと、そのオブジェクトのコピーが作成され、 からアクセスします。これはコピーなので、最初はfunc func str "Lele" main` が示すのと同じ値も含まれています。str
"Lala"
func
func
"Lala", which
then outputs. Then the assignment in
changes the
member *of that copy* to
and outputs that. The original object is not affected as the output in
ご覧のとおり、複数のオブジェクトが存在する可能性があり、アクセスしたいオブジェクトのstr
メンバーを指定することが重要です。
呼び出された関数でオブジェクトを変更するつもりがない場合、コピーを作成するのは時間の無駄です。したがって、それを const への参照として渡すこともできます。
void func(C const& object)
{
std::cout << object.str << std::endl;
}
int main()
{
C main_object;
main_object.str = "Lala";
func(main_object);
}
引数C const&
は、「呼び出し元から渡されたオブジェクトに直接アクセスしたいが、変更しないことを約束する」と述べています。「直接アクセスする」部分は で示され、&
「変更しないことを約束します」は で示されますconst
。コンパイラーは実際に、あなたが約束を守っていることをチェックし、それを変更しようとしない (つまり、あなたが約束を変更しようとした場合func
、object.str = "Lele"
コンパイラーは文句を言うでしょう (コンパイラーにそれについて黙るように指示する方法はありますが、そうすべきです) t do that; just keep your promise). ただし、これはその特定のオブジェクトにのみ適用されることに注意してください; たとえば、次のコードは完全に問題ありません:
void func(C const& object)
{
C another_object;
another_object.str = "Lele";
std::cout << object.str << " -- " << another_object.str << std::endl;
}
int main()
{
C main_object;
main_object.str = "Lala";
func(main_object);
}
Lala -- Lele
別のオブジェクトを再度扱っているため、これによりエラーは発生せず、出力されます。
もちろん、渡されたオブジェクトを変更したい場合もあるでしょう。次に、&
なしで使用できますconst
:
void func(C& object)
{
std::cout << object.str << " -- ";
object.str = "Lele";
std::cout << object.str << " -- ";
}
int main()
{
C main_object;
main_object.str = "Lala";
func(main_object);
std::cout << object.str << std::endl;
}
これは印刷されLala -- Lele -- Lele
ます。
ここで再び から引数として渡されたオブジェクトに直接アクセスしますがmain
、今回は変更しないことを約束せず、実際に変更します。からの出力はmain
、実際main_object
に変更されたことを示しています。
変数を静的メンバーにする
ここで、そのタイプのオブジェクトごとに個別のものではなく、に1 つだけ存在することを本当に望む可能性があります。これがあなたが望むものであると絶対に確信しているなら、クラスの静的メンバーを作ることができます:str
C
str
class C
{
private:
static std::string str; // note the keyword "static" here
friend void func();
};
std::string C::str; // You have to have an extra definition for class static variables!
str
これで、利用可能なオブジェクトがなくてもアクセスできC
ます。ただし、内部にアクセスしたいことを内部のコンパイラに伝える必要があることに注意してください。func
str
C
void func()
{
C::str = "Lala";
std::cout << C::str << std::endl;
}
オブジェクトのメンバーであるかのように、オブジェクトの変数にアクセスすることもできます。ただし、これは、さまざまなオブジェクトが独自の を持っているという意味ではないことに注意してstr
ください。たとえば、変更されたクラス定義では、上記のコードの動作が異なります。
void func()
{
C object1, object2;
object1.str = "Lala";
object2.str = "Lele";
std::cout << object1.str << " -- " << object2.str << "\n";
}
オブジェクトに依存しない がLele -- Lele
1 つしかないため、出力を取得します (この場合の構文は、その点で誤解を招きます。実際には、 「 の型に対して定義された 、つまり」を意味します)。str
object1.str
str
object1
C