特に明記しない限り、C ++のすべてのオブジェクトは変更可能ですか?
PythonとJavaScriptでは、文字列、タプル、ユニコードを変更できません。C ++に不変なものがあるかどうか、またはすべてのオブジェクトが変更可能であるかどうかを疑問に思っていましたconst
。不変にするために型修飾子を使用する必要があります。
特に明記しない限り、C ++のすべてのオブジェクトは変更可能ですか?
PythonとJavaScriptでは、文字列、タプル、ユニコードを変更できません。C ++に不変なものがあるかどうか、またはすべてのオブジェクトが変更可能であるかどうかを疑問に思っていましたconst
。不変にするために型修飾子を使用する必要があります。
回答:
不変性は以前からよく理解されてきました。Python、Java、およびC ++には、直接比較を困難にする異なるメモリモデルがあります。最初に引用した記事の著者は、C ++を知らないようです。
Python、Java、およびほとんどのマルチパラダイム言語と同様に、CおよびC ++はデフォルトで可変性を許可します。これは、プログラマーが通常望んでいることです。String x
変数がある場合、新しい値を割り当てられるようにしたいのですx = "foo"
。
CおよびC ++のconstシステムは、Python、Java、さらにはScalaに欠けている多くの微妙な不変性を可能にします。C ++関数がa const std::string&
またはaを取る場合、const char*
この関数はその文字列の内容を変更しない(できない)ことを約束します(そしてコンパイラはある程度保証します)。constオブジェクトを指定すると、constとしてマークされているそのオブジェクトのメソッドのみを呼び出すことができます。C ++クラスのパブリックメンバーがのみの場合const
、オブジェクトは事実上不変です。
ただし、CおよびC ++のオブジェクトはメモリロケーションであり、変数はメモリロケーションの名前であるため、これは混乱を招くことがあります。対照的に、PythonおよびJavaの変数は、オブジェクトへのポインターの名前です。参照セマンティクスを持つ言語でx = y
は、「xがyと同じオブジェクトを指すようにする」ことを意味します。ポインターをコピーするだけなので、これは不変オブジェクトで可能です。C ++のような値のセマンティクスを持つ言語では、それは「の内容を更新する意味x
の内容がy
」。したがって、CまたはC ++で変数の再割り当てが必要な場合は、変数にconst型がない可能性があります。不変オブジェクトでこれを行うには、明示的にポインターを使用する必要があります。
JavaとPythonが不変の文字列オブジェクトを使用することは基本的な設計上の決定ですが、マルチスレッド環境での不変性の利点とは直接関係していません。1つの理由は、ソースコードの文字列リテラルをプールできるため、オブジェクトの数が減ることです。これはC / C ++でも可能です。C ++では、リテラル"foo"
に型がありますconst char[4]
(4番目の文字が終了文字です'\0'
)。もう1つの理由は、dicts / mapのセットおよびキーのエントリは変更してはならないことです。文字列はdictキーとして広く使用されているため(ほとんどのPythonオブジェクトはdictです)、不変性により一般的なエラーの原因が取り除かれます。Javaでは、不変文字列のもう1つの理由はJavaセキュリティモデルです。これらの理由はすべて、マルチスレッドとはまったく無関係です。
Javaが不変性を念頭に置いて構築されている場合、言語の外観は大きく異なります。それはC ++に密接に影響を受けていますが、デザイナーはより単純な言語を作成するために一生懸命努力しました。constを取り除くことはそのようなステップの1つです。C ++のconst参照と同等のJavaのものは、任意の変更メソッドをとして実装しthrows new NotImplementedException()
、非変更メソッド呼び出しを実際のコレクションに転送するアダプターまたはデコレーターです。java.utilコレクションインターフェースがすべて可変性を暗示しているという事実は、不変第一言語を目指して努力しなかった明確な兆候です。
Javaが並行性の問題を解決するために提唱したソリューションは、不変性ではなく、広範なロックでした。すべてのオブジェクトには、synchronized
ブロックまたはメソッド全体で使用できるミューテックスが含まれています。結局のところ、これはパフォーマンスに適しておらず、十分にスケーリングされず、エラーが発生しやすく、変更可能なグローバル状態に対処する必要があります。
mut
キーワードが必要であり、不必要な可変性がコンパイラーによってフラグが立てられる場合)は、私の仮説をサポートする傾向があります。Rustには、let
バインドよりも多くのバインディングがありますlet mut
。
for x in 1..10
はlet mut
、がループ変数の定義に使用していないためですが、明らかに(ループレベルのみで、ループ本体内ではなく)可変性が必要です。
const T o = [&]{T o; o.init(...); return o;}();
、不変性を念頭に置いて設計されていないAPIについて、すぐに呼び出されるラムダを使用しましょう。
ほとんど。言語自体はconst
、を使用しない限りすべてを可変として扱いますが、単に変更する方法を提供しないだけで、コンパイラに不変であると通知せずに不変オブジェクトを構築することは可能です。
たとえば、次のクラスを見てください。
class MyClass {
int value;
public:
MyClass(int v) : value(v) {}
int getValue() {return value;}
MyClass &operator=(const MyClass &other) = delete;
};
のインスタンスMyClass
は不変です。なぜなら、それらを変更する方法がないからです。しかし、コード内のどこにも、それらが不変であると実際に記述されていません。(これは、JavaのString
クラスのインスタンスが不変であるのと同じ方法です)
delete
ここでコピー代入演算子を使用すると、移動代入演算を介して右辺値参照から代入できないことも保証されますか?
value
を宣言しgetValue()
、const関数を宣言すると役立つでしょう。