3

以下は、文字列クラスの抽象化です。

class string {
public:
    string(int n = 0) : buf(new char[n + 1]) { buf[0] = '\0'; }
    string(const char *);
    string(const string &);
    ~string() { delete [] buf; }

    char *getBuf() const;
    void setBuf(const char *);
    string & operator=(const string &);
    string operator+(const string &);
    string operator+(const char *);

    private:
       char *buf;
    };

    string operator+(const char *, const string &);
    std::ostream& operator<<(std::ostream&, const string&);

これら2つの演算子が関数をオーバーロードした理由を知りたい

  string operator+(const char *, const string &);
  std::ostream& operator<<(std::ostream&, const string&);

クラスメンバー関数またはフレンド関数ではありませんか? 2 パラメーター演算子のオーバーロードされた関数は一般的にフレンド関数であることは知っていますが (よくわかりませんが、これについても教えていただければ幸いです)、私の教授もそれらをフレンドとして宣言しませんでした。以下は、これらの関数の定義です。

 string operator+(const char* s, const string& rhs) {

           string temp(s);
           temp = temp + rhs;
           return temp;
 }

 std::ostream& operator<<(std::ostream& out, const string& s) {
     return out << s.getBuf();
 }

誰かがこれを小さな例で説明したり、同様の質問に誘導したりできますか? 前もって感謝します。よろしく

4

4 に答える 4

2

演算子 + について話しましょう。非メンバーとして持つと、次のようなコードが可能になります

string s1 = "Hi";
string s2 = "There";
string s3;

s3 = s1 + s2;
s3 = s1 + "Hi";
s3 = "Hi" + s1;

operator+ が名前空間スコープ関数ではなくメンバーである場合、最後の代入ステートメントは使用できません。ただし、名前空間スコープ関数の場合、文字列リテラル "Hi" は、変換コンストラクター "string(const char *);" を使用して一時的な文字列オブジェクトに変換されます。operator+ に渡されます。

あなたの場合、プライベートメンバー「buf」のアクセサーがあるため、この関数を友達にせずに管理することができました。しかし、通常、何らかの理由でそのようなアクセサーが提供されない場合、これらの名前空間スコープ関数はフレンドとして宣言する必要があります。

それでは、演算子 << について話しましょう。

これは、ostream オブジェクトに対して定義された挿入演算子です。ユーザー定義型のオブジェクトを出力する必要がある場合は、ostream クラス定義を変更する必要がありますが、これはお勧めできません。

したがって、オペレーターは名前空間スコープでオーバーロードされます。

どちらの場合も、Koenig ルックアップとも呼ばれるこれらの名前空間スコープ関数のルックアップの背後にある主要な理由である、引数依存ルックアップのよく知られた原則があります。

別の興味深い読み物はNamespace Interface Principleです

于 2013-01-24T03:47:32.877 に答える
2

キーワードは、およびのメンバーへのfriendアクセスを許可します。これらの関数は;の内部を使用する必要がないため、例では使用されていません。インターフェースで十分です。protectedprivateclassstringpublic

friendclass {}関数は、スコープ内で定義されている場合でも、クラスのメンバーになることはありません。これはかなり紛らわしいです。中括弧friend内で非メンバー関数を定義するためのトリックとして使用されることがあります。class {}しかし、あなたの例では、特別なことは何も起こっていません.2つの機能だけです. そして、関数はたまたま演算子のオーバーロードです。

一部のオーバーロードをメンバーとして定義し、もう 1 つを非メンバーとして定義するの不適切なスタイルです。operator+それらをすべて非メンバーにすることで、インターフェースが改善されます。オーバーロード関数の内部になる左側の引数には、異なる型変換規則が適用されるため、this混乱を招くバグが発生する可能性があります。したがって、可換演算子は通常、非メンバー (friendまたは非メンバー) である必要があります。

于 2013-01-24T03:54:07.270 に答える
1

演算子は、メンバー関数およびスタンドアロン (通常の) 関数によってオーバーロードできます。スタンドアロンのオーバーロード関数が友達であるかどうかはまったく関係ありません。フレンドシップ プロパティは、演算子のオーバーロードとはまったく関係ありません。

スタンドアロン関数を使用する場合、クラスの「非表示」(プライベートまたは保護) 内部に直接アクセスする必要がある場合がありますfriend。この種の特権アクセスが必要ない場合 (つまり、クラスのパブリック インターフェイスに関して必要な機能を実装できる場合)、関数を として宣言する必要はありませんfriend

それだけです。

スタンドアローンのオーバーロード関数をフレンドとして宣言することは非常に一般的になり、人々はそれを「フレンド関数によるオーバーロード」と呼ぶことがよくあります。上で述べたように、友情自体はそれとは何の関係もないので、これは本当に誤解を招く誤称です.

friendまた、クラスへの特権アクセスが必要ない場合でも、オーバーロード関数を宣言することがあります。friend関数宣言がクラス定義のすぐ内側に関数の即時インライン定義を組み込むことができるため、彼らはそれを行います。そうしないfriendと、別の宣言と別の定義を行う必要があります。コンパクトなインライン定義は、場合によっては「よりクリーン」に見えるかもしれません。

于 2013-01-24T04:04:12.037 に答える
0

私はC++のオーバーロードに少し慣れていませんが、この簡単なメモで上記の回答を完成させます:

  • 左側のオペランドの型がユーザー定義型(たとえば、クラス) である場合、演算子のオーバーロードをメンバー関数として実装する必要があります (ただし、実装する必要はありません)。そして、これらのオーバーロード (おそらく++=++ ... のようなもの) が左側のオペランドを変更する場合、呼び出し元の型 (実際には変更されたオブジェクト) の参照を返すことに注意してください。 . そのため、たとえば Coplien の標準的な形式では、operator=オーバーロードはメンバー関数であり、"UserClassType &" を返します (実際には関数が を返すため*this)。

  • 左側のオペランドの型がシステム型( intostreamなど) の場合、演算子のオーバーロードをスタンドアロン関数として実装する必要があります。

ちなみに、friendキーワードは悪い、醜い、子供を食べる、とずっと言われてきました。主にコーディングスタイルの問題だと思いますが、使用するときは注意して、できる限り避けることをお勧めします。(私はまだその使用が義務付けられている状況に直面したことがないので、本当にわかりません!)

(そして、私の下手な英語で申し訳ありませんが、私も少し錆びています)

サイ

于 2013-01-24T14:52:38.990 に答える