5

私が正しければ適合し、より良い解決策があるかどうか教えてください:

のような定数メンバーを持つオブジェクトint const width;は、コンパイラによって暗黙的に作成される合成代入演算子では処理できないことを理解しています。しかし、QList(そして私はstd :: listもそうだと思います)には、実用的な代入演算子が必要です。したがって、定数メンバーとQListを持つオブジェクトを使用する場合、次の3つの可能性があります。

  1. 定数メンバーは使用しないでください。(解決策ではありません)
  2. 自分の代入演算子を実装します。
  3. 代入演算子を必要としない他のコンテナを使用する

あれは正しいですか?他にエレガントな解決策はありますか?

また、私は私ができるかどうか疑問に思います:

  • (4)コンパイラに定数メンバーを処理する代入演算子を作成させます!(なぜこれがそんなに大きな問題なのかわかりません。なぜオペレーターは初期化リストを内部で使用するのに十分な知性を持っていないのですか?それとも何かが足りないのですか?)
  • (5)リストで代入演算を使用しないことをQListに伝えます。

編集:私はこのクラスのオブジェクトを自分で割り当てることはありません。これらは、コピーコンストラクターまたはオーバーロードされたコンストラクターによってのみ作成されます。したがって、代入演算子は、私自身ではなく、コンテナによってのみ必要とされます。

EDIT2:これは私が作成した代入演算子です。それが正しいかどうかはわかりませんが。Cellには2つのパラメーターコンストラクターがあります。これらのパラメーターは、初期化リストを使用して2つの定数メンバーを設定します。ただし、オブジェクトには他の変数(非const)メンバーも含まれています。

Cell& Cell::operator=(Cell const& other)
{
 if (this != &other) {
  Cell* newCell = new Cell(other.column(), other.row());
  return *newCell;
 }
 return *this;
}

EDIT3:私はこのスレッドをほぼ同じ質問で見つけました:C ++:constクラスメンバーとのSTLトラブルすべての答えを組み合わせて私の質問に答えました。

4

4 に答える 4

8

あなたはおそらく C++ の初心者であり、Python、Java、または C# のように動作することを期待しています。

不変の Java オブジェクトをコレクションに入れることは非常に一般的です。これが機能するのは、Java では実際に Javaオブジェクトをコレクションに入れるのではなく、単に Javaオブジェクトを参照する Java参照を入れるだけだからです。さらに正確に言うと、コレクションは内部的に Java 参照変数で構成されており、これらの Java 参照変数に割り当てても、参照される Java オブジェクトにはまったく影響しません。彼らは気づいていません。

「オブジェクト」、「参照」、「変数」という用語は、C++ ではまったく異なる意味を持っているため、意図的に「Java オブジェクト」、「Java 参照」、「Java 変数」と言いました。可変T変数が必要な場合は、可変Tオブジェクトが必要です。変数とオブジェクトは基本的に C++ では同じものだからです。

変数は、オブジェクトの宣言によって導入されます。変数の名前はオブジェクトを示します。

C++ では、変数にオブジェクトは含まれません。オブジェクトです。変数への代入は、(メンバー関数を呼び出すことによってoperator=) オブジェクトを変更することを意味します。それを回避する方法はありません。不変オブジェクトがある場合、型システムを明示的に弱体化させないと代入は機能しa = b ない可能性があり、そうすると、オブジェクトが不変であることについて事実上クライアントに嘘をついたことになります。約束をして故意に破るのは、むしろ無意味ですよね?

もちろん、Java の方法を単純にシミュレートすることもできます。不変オブジェクトへのポインターのコレクションを使用します。これが効果的な解決策であるかどうかは、オブジェクトが実際に何を表しているかによって異なります。ただし、これが Java でうまく機能するからといって、C++ でもうまく機能するとは限りません。C++ には不変値オブジェクト パターンなどはありません。Java では良い考えですが、C++ ではひどい考えです。

ところで、代入演算子は完全に非慣用的であり、メモリ リークを引き起こします。C++ の学習に真剣に取り組んでいる場合は、これらの書籍のいずれかを読む必要があります。

于 2010-11-26T21:38:16.607 に答える
3

const「この値は特別な状況でのみ変更できる」という意味ではありません。むしろ、constは、「それを使って許可されていることは、(観察できるように)何らかの方法で変更を引き起こすことはありません」という意味です。

const修飾変数がある場合、コンパイラーのフラット(およびconstそもそもそれを修飾するためのあなた自身の選択)によって、それを変更させるようなことをすることは許可されていません。それが何をするかconstです。それが非constオブジェクトへのconst参照である場合、またはその他のさまざまな理由で、アクションに関係なく変更される可能性があります。プログラマーとして、指示対象が実際には一定ではないことを知っている場合は、それを捨ててconst_cast変更することができます。

しかし、あなたの場合、定数メンバー変数では、これは不可能です。const修飾変数は、非constへのconst参照にすることはできません。これは、それがまったく参照ではないためです。

編集:これが何であるか、そしてconstの正当性に関して自分自身を振る舞うべき理由のスリリングな例については、実際のコンパイラーが実際に何をするかを見てみましょう。この短いプログラムを考えてみましょう。

int main() {
  const int i = 42; 
  const_cast<int&>(i) = 0; 
  return i;
}

そして、LLVM-G++が出力するものは次のとおりです。

; ModuleID = '/tmp/webcompile/_2418_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-linux-gnu"

define i32 @main() nounwind {
entry:
  %retval = alloca i32                            ; <i32*> [#uses=2]
  %0 = alloca i32                                 ; <i32*> [#uses=2]
  %i = alloca i32                                 ; <i32*> [#uses=2]
  %"alloca point" = bitcast i32 0 to i32          ; <i32> [#uses=0]
  store i32 42, i32* %i, align 4
  store i32 0, i32* %i, align 4
  store i32 42, i32* %0, align 4
  %1 = load i32* %0, align 4                      ; <i32> [#uses=1]
  store i32 %1, i32* %retval, align 4
  br label %return

return:                                           ; preds = %entry
  %retval2 = load i32* %retval                    ; <i32> [#uses=1]
  ret i32 %retval2
}

特に興味深いのは線store i32 0, i32* %i, align 4です。これは、const_castが成功したことを示しています。実際には、初期化された値にゼロを割り当てました。

ただし、const修飾を変更しても、目に見える変化は生じません。したがって、GCCは、42を%0に入れ、その42を%1に配置し、それを再び%retvalに格納してから、%retval2にロードするというかなり長いチェーンを生成します。iしたがって、G ++はこのコードで両方の要件を満たし、constはキャストされましたが、メインリターン42 に目に見える変化はありませんでした。


たとえば、標準コンテナの要素など、変更可能な値が必要な場合は、は必要ありませんconst

private: パブリックゲッターメソッドとプライベートセッターメソッドでメンバーを使用することを検討してください。

于 2010-11-26T20:21:07.477 に答える
3

(4)はオプションではありません。暗黙的に宣言されたコピー代入演算子は、右側のオブジェクトの各メンバーを左側のオブジェクトの同じメンバーに割り当てます。

コンパイラは、これが無効であるのと同じ理由で、const 修飾されたデータ メンバーを持つクラスのコピー代入演算子を暗黙的に生成できません。

const int i = 1;
i = 2;

(2)は、この同じ問題をどうにかして克服しなければならないため、問題があります。

(1)は明らかな解決策です。クラス型に const 修飾されたデータ メンバーがある場合、それは代入可能ではなく、代入はあまり意味がありません。なぜこれが解決策ではないと言うのですか?


クラス型を割り当て可能にしたくない場合は、その値型が割り当て可能であることを必要とするコンテナーで使用できません。すべての C++ 標準ライブラリ コンテナーには、この要件があります。

于 2010-11-26T19:58:09.477 に答える
2

答えをまとめてみます。

主な問題は、QList が内部的に代入を使用するため、代入演算子が存在する必要があることです。したがって、実装とインターフェイスを混在させます。したがって、代入演算子 QList は必要ありませんが、それなしでは機能しません。ソース

@ 3. std::List はありますが、要素への一定時間アクセスを提供しませんが、QList は提供します。

@ 2. コピー コンストラクターと目的のプロパティで新しいオブジェクトを作成し、それを返すことで可能です*。const プロパティを回避することはできますが、const をまったく使用しないよりはまだ良いです。なぜなら、コンテナがここで不正行為を行うことを許可する一方で、ユーザーが自分でこれを行うのを防ぐことができるからです。これは、このメンバーを定数にする本来の意図でした。

ただし、オーバーロードされた代入演算子を作成すると、コードが複雑になり、メンバーの const 化が最初に解決するよりも多くのエラーが発生する可能性があることを考慮してください。

@ 1. 結局、これが最も簡単な解決策のようです。プライベートである限り、オブジェクト自体が変更されないことに注意する必要があります。

@ 4. 彼を強制する方法はありません。this->row = other.row変数は定数であり、ある時点でint const row;以前に定義されたものを使用する必要があるため、彼はその方法を知りません。そして const はこの場合でも定数を意味します。1 つのソース

@ 5 QList には、この種のオプションはありません。

追加の解決策:

  • 純粋なオブジェクトの代わりにオブジェクトへのポインターを使用する

※現時点では不明です。

于 2010-11-26T22:10:50.840 に答える