Java、Javascript、C#などのメモリ管理言語が「new」キーワードを保持しているのはなぜですか?


139

newJava、Javascript、C#などの言語のキーワードは、クラスの新しいインスタンスを作成します。

この構文は、C ++から継承されているようです。C++ newは、ヒープ上のクラスの新しいインスタンスを割り当て、新しいインスタンスへのポインターを返すために特に使用されます。C ++では、これがオブジェクトを構築する唯一の方法ではありません。new- を使用せずにスタック上にオブジェクトを作成することもできます。実際、この方法のオブジェクトの作成方法はC ++でより一般的です。

だから、C ++のバックグラウンドから来て、newJava、Javascript、C#のような言語のキーワードは、私にとって自然で明白なように思えました。それから、newキーワードのないPythonを学び始めました。Pythonでは、次のように、コンストラクターを呼び出すだけでインスタンスが構築されます。

f = Foo()

最初は、Pythonが持つ理由がないことに気付くまで、これは少しnew戸惑いました。すべてがオブジェクトであるため、さまざまなコンストラクタ構文を明確にする必要はありません。

しかし、それから私は考えました-Javaの本当のポイントは何newですか?なんで言うのObject o = new Object();?どうしてObject o = Object();?C ++ではnew、ヒープへの割り当てとスタックへの割り当てを区別する必要があるため、間違いなくが必要ですが、Javaではすべてのオブジェクトがヒープ上に構築されますnew。Javascriptについても同じ質問をすることができます。私があまり馴染みのないC#では、newオブジェクト型と値型を区別するという点で何らかの目的があると思いますが、よくわかりません。

とにかく、C ++の後に来た多くの言語は単にnewキーワードを「継承」しているように思えます-本当にそれを必要としません。それはほとんど痕跡のキーワードのようなものです。なんらかの理由でそれを必要としているようには見えませんが、まだあります。

質問:これについて正しいですか?あるいはnew、Java、Javascript、C#などのC ++にインスパイアされたメモリ管理言語である必要があり、Pythonではなくてはならない説得力のある理由がありますか?


13
問題は、どの言語にnewキーワードがあるのか​​ということです。もちろん、新しい変数、愚かなコンパイラを作成したいです!良い言語は、私の意見に似た構文を持っているでしょうf = heap Foo()f = auto Foo()

7
考慮すべき非常に重要な点は、言語が新しいプログラマーにどの程度馴染みがあるかです。Javaは、C / C ++プログラマーに馴染みがあるように明示的に設計されました。

1
@Lundin:それは本当にコンパイラ技術の結果です。最新のコンパイラーはヒントさえ必要としません。その変数の実際の使用に基づいて、オブジェクトを配置する場所がわかります。fつまり、関数をエスケープしない場合、スタックスペースを割り当てます。
-MSalters

3
@Lundin:それは可能であり、実際、最新のJVMでも可能です。f囲んでいる関数が戻ったときにGCがオブジェクトを収集できることを証明するだけの問題です。
MSalters

1
特にそのデザインが明確な利益のために4つの余分な文字を入力するように要求する場合、言語がそうであるように設計されている理由を尋ねることは建設的ではありませんか?私は何が欠けていますか?
MatrixFrog

回答:


94

あなたの観察は正しいです。C ++は複雑な獣であり、このnewキーワードは、delete後で必要なものと自動的に回収されるものを区別するために使用されました。JavaとC#ではdelete、ガベージコレクターが自動的に処理するため、キーワードを削除しました。

問題は、なぜ彼らがnewキーワードを保持したのですか?言語を書いた人々と話をせずに答えるのはちょっと難しい。私の最良の推測は以下のとおりです。

  • それはだった意味的に正しいです。C ++に精通していれば、newキーワードがヒープ上にオブジェクトを作成することを知っていました。それでは、なぜ予想される動作を変更するのでしょうか?
  • メソッドを呼び出すのではなく、オブジェクトをインスタンス化しているという事実に注意を促します。Microsoftコードスタイルの推奨事項では、メソッド名は大文字で始まるため、混乱する可能性があります。

Rubyは、PythonとJava / C#の間のどこかにありnewます。基本的に、次のようなオブジェクトをインスタンス化します。

f = Foo.new()

これはキーワードではなく、クラスの静的メソッドです。つまり、シングルトンが必要な場合は、デフォルトの実装をオーバーライドして、new()毎回同じインスタンスを返すことができます。必ずしも推奨されるわけではありませんが、可能です。


5
Ruby構文は見栄えが良く、一貫性があります!C#の新しいキーワードは、デフォルトコンストラクターを持つ型の汎用型制約としても使用されます。
スティーブンジュリス

3
はい。ただし、ジェネリックについては説明しません。JavaとC#のジェネリックバージョンはどちらも非常に鈍いです。ただし、C ++を理解するほうがはるかに簡単です。ジェネリックは、静的に型付けされた言語でのみ有効です。Python、Ruby、Smalltalkなどの動的に型付けされた言語は、それらを必要としません。
ベリンロリチュ

2
「= TFoo.Create(PARAM1、PARAM2 ...):Delphiは( `Fを作成呼ばれるコンストラクタは通常、(だけBHY大会)ことを除いて、ルビーに似た構文を持って
ジェリー

Objective-Cでは、allocメソッド(Rubyのものとほぼ同じnew)もクラスメソッドです。
ミパディ

3
言語設計の観点から見ると、キーワードは多くの理由で良くありません。主にそれらを変更することはできません。一方、メソッドの概念を再利用する方が簡単ですが、解析と分析中に注意が必要です。Smalltalkとその類はメッセージを使用し、C ++とその類はキーワードを使用します
ガブリエルシュチェルバク

86

要するに、あなたは正しい。キーワードnewは、JavaやC#などの言語では不要です。1990年代にC ++ Standard Comiteeのメンバーであり、後にJavaに関する本を出版したBruce Eckelからの洞察を以下に示します。http ://www.artima.com/weblogs/viewpost.jsp?thread=260578

ヒープオブジェクトとスタックオブジェクトを区別する何らかの方法が必要でした。この問題を解決するために、新しいキーワードがSmalltalkから割り当てられました。スタックオブジェクトを作成するには、Cat xのように宣言するだけです。または、引数付きで、Cat x( "mittens");。ヒープオブジェクトを作成するには、new Catのようにnewを使用します。または新しい猫(「ミトン」);。制約を考えると、これはエレガントで一貫したソリューションです。

すべてのC ++の処理がひどく複雑すぎると判断した後、Javaを使い始めてください。ここで皮肉なことに、Javaはスタック割り当てを破棄するという決定を下すことができたし、実際に行ったということです(先ほど触れたプリミティブの問題を無視して)。また、すべてのオブジェクトはヒープに割り当てられるため、スタックとヒープの割り当てを区別する必要はありません。Cat x = Cat()またはCat x = Cat( "mittens")と簡単に言うことができます。または、さらに良いことに、型推論を組み込んで繰り返しを排除します(ただし、それとクロージャーなどの他の機能は「長すぎる」ため、代わりにJavaの平凡なバージョンに固執します。型推論については説明しましたが、 Javaに新機能を追加する際の問題を考えると、そうなるはずはありません。


2
Stack vs Heap ...洞察力に富んでいて、考えたこともないでしょう。
WernerCD

1
「型推論は議論されましたが、私はそれが起こらない可能性があります。」ただし、これがJava 10の唯一のフラッグシップであることは興味深いです。varキーワードは、として良いとしてどこにも近く、autoC ++で、私はそれが改善することを願っています。
パトリック

15

私が考えることができる2つの理由があります:

  • new オブジェクトとプリミティブを区別します
  • new構文はもう少し読みやすい(IMO)

1つ目は簡単です。どちらにせよ、この2つを区別するのは難しくないと思います。2番目はOPのポイントの例ですnew。これは一種の冗長です。

ただし、名前空間の競合が発生する可能性があります。考慮してください:

public class Foo {
   Foo() {
      // Constructor
   }
}

public class Bar {
   public static void main(String[] args) {
      Foo f = Foo();
   }

   public Foo Foo() {
      return Foo();
   }
}

想像力を過度に引き伸ばさずに、無限に再帰的な呼び出しを簡単に行うことができます。コンパイラは、「オブジェクトと同じ関数名」エラーを強制する必要があります。これは本当に害にはなりませんがnewGo to the object of the same name as this method and use the method that matches this signature.Java を使用する方がはるかに簡単です。Javaは解析するのが非常に簡単な言語であり、この分野で役立つと思われます。


1
これは他の無限再帰とどのように違いますか?
DeadMG

それは起こる可能性のある悪いことのかなり良い例ですが、それを行う開発者にはいくつかの深刻な精神的な問題があります。
Tjaart

10

Javaは、1のために、まだC ++の二分法がありますいない、非常にすべてが対象です。Javaには、動的に割り当てる必要のない組み込み型(charおよびなどint)があります。

それはそれが意味するものではありませんnew動的に割り当てする必要はありません種類のセットが固定され、知られている-しかし、Javaで本当に必要です。オブジェクトを定義すると、コンパイラは、それが単純な値であるcharか、動的に割り当てられる必要があるオブジェクトであるかを知ることができます(実際、知っています)。

Javaのデザインの品質(または場合によってはその欠如)について、これが何を意味するのかについては(まだ)控えています。


要するに、プリミティブ型はコンストラクター呼び出しをまったく必要としません。これらは、リテラルとして記述されているか、演算子から、またはプリミティブを返す関数から来ています。実際には、(ヒープ上にある)文字列もなしで作成するnewため、これは実際には理由ではありません。
パウロEbermann

9

JavaScriptでは、コンストラクターは通常の関数のように見えるので、それが必要です。それで、新しいキーワード用ではない場合、JSは新しいオブジェクトを作成したいことをどのように知る必要がありますか


4

興味深いことに、VB それを必要とします-の区別

Dim f As Foo

Foo f;C#と同等の変数を割り当てずに宣言します。

Dim f As New Foo()

変数を宣言し、クラスの新しいインスタンスを割り当てます。これはvar f = new Foo();、C#に相当するものであり、実質的な違いです。.NET VB以前では、コンストラクターにオーバーロードがなかったため、Dim f As FooまたはDim f As New Foo-を使用することさえできました。


4
VBには簡略版は必要ありません。これは単なる略語です。タイプとコンストラクターDim f As Foo = New Foo()を使用して、より長いバージョンを書くこともできます。C#の変数に最も近いOption Infer Onを使用すると、Dim f = New Foo()と記述でき、コンパイラはfの型を推測します。
MarkJ

2
...そしてDim f As Foo()配列を宣言しFooます。よかった!:-)
ハインジ

@MarkJ:vb.netでは、略記法は同等です。vb6では、そうではありませんでした。vb6では、while(つまり)が読み込まれた場合にaを構築するプロパティをDim Foo As New Bar効果的に作成Fooします。この動作は1回だけ発生するものではありません。に設定してから読み取ると、別の新しいが作成されます。BarNothingFooNothingBar
supercat

4

私は多くの答えが好きで、これを追加したいと思います:
それはコードの読み取りを容易にし
ます C#やその他の言語では、当然、object予約語が使用されます。しかし、そうでない場合Foo = object()は、オブジェクトメソッド呼び出しの結果であるか、新しいオブジェクトをインスタンス化しています。newキーワードのない言語にはこの状況に対する保護があるとnewいいのですが、コンストラクターを呼び出す前にキーワードの要件があることで、オブジェクトと同じ名前のメソッドの存在を許可します。


4
おそらく、これを知ることは悪い兆候です。
DeadMG

1
@DeadMG:「ほぼ間違いなく」とは「バーが閉じるまでそれを主張する」という意味です…
ドナルドフェローズ

3

多くのプログラミング言語には多くの痕跡があります。時にはそれが設計によってそこにあります(C ++はできるだけCと互換性があるように設計されました)、時にはそこにあります。より悪質な例として、壊れたC switchステートメントがどこまで伝播したかを考えてください。

私はJavascriptとC#を十分に理解していませんがnew、C ++がそれを持っていることを除いて、Javaに理由はありません。


JavaScriptは、特にJavaに似ていない何かをしている場合でも、「Javaのように見える」ように設計されていると思います。x = new Y()JavaScriptではありませんインスタンス化Y JavaScriptがいないため、クラスを持つクラスを。しかし、Javaのように見えるのでnew、Javaからキーワードを引き継いでいます。JavaはもちろんC ++から引き継いでいます。
MatrixFrog

壊れたスイッチに対して+1(それを修復することを期待して:D)。
-maaartinus

3

C#では、メンバーによって隠されている可能性のあるコンテキストで型を表示できます。は、そのタイプと同じ名前のプロパティを持つことができます。以下を考慮してください。

class Address { 
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

class Person {
    public string Name { get; set; }
    public Address Address { get; set; }

    public void ChangeStreet(string newStreet) {
        Address = new Address { 
            Street = newStreet, 
            City = Address.City,
            State = Address.State,
            Zip = Address.Zip
        };
    }
}

使用は注意してくださいnew明らかにすることができますAddressですAddressタイプではないAddressメンバー。これにより、メンバーはそのタイプと同じ名前を持つことができます。これがないと、名前の衝突を避けるために、タイプの名前の前に付ける必要がありますCAddress。これは非常に意図的なものであり、Andersはハンガリーの表記法などを好まなかったため、C#を使用せずに使用できるようにしたいと考えていました。それもおなじみだったという事実は二重ボーナスでした。


w00t、およびAddressは、Addressと呼ばれるインターフェイスから派生する可能性があります。そのため、同じ名前を再利用でき、コードの一般的な可読性が低下するという利点はあまりありません。そのため、インターフェイスのIを保持し、クラスからCを削除することにより、実用的なメリットがまったくない悪臭が発生しました。
gbjbaanb

彼らは「新しい」の代わりに「新しい」を使用しないのだろうか。誰かが小文字もあると彼らに伝えることができ、それがこの問題を解決します。
maaartinus

C#などのC派生言語の予約語はすべて小文字です。文法では、あいまいさを避けるためにここで予約語が必要です。したがって、「新規」ではなく「新規」を使用します。
chuckj

@gbjbaanb臭いはありません。型を参照しているか、プロパティ/メンバーを参照しているか、関数を参照しているかは常に明確です。本当の匂いは、クラスと同じ名前空間に名前を付けるときです。MyClass.MyClass
スティーブン

0

興味深いことに、完全な転送の出現によりnew、C ++でも非配置はまったく必要ありません。そのため、おそらく、前述の言語のいずれでも必要ありません。


4
あなたは何を楽しみにしていますか?最終的にstd::shared_ptr<int> foo();は、new int何らかの形で電話する必要があります。
-MSalters

0

Javaデザイナーがこれを念頭に置いていたかどうかはわかりませんが、オブジェクトを再帰的なレコードと考えると、オブジェクトFoo(123)は返されず、作成されるオブジェクトを固定点とする関数(つまり、返される関数)が想像できます構築中のオブジェクトを引数として指定した場合はオブジェクト)。そして、その目的newは「結び目を作る」ことであり、その不動点を返すことです。言い換えれnewば、「対象」にそのことを認識させselfます。

この種のアプローチは、たとえば継承を形式化するのに役立ちます。別のオブジェクト関数でオブジェクト関数を拡張し、最終的にそれらをself使用して共通を提供できますnew

cp = new (Colored("Blue") & Line(0, 0, 100, 100))

ここで&は、2つのオブジェクト関数を組み合わせています。

この場合new、次のように定義できます。

def new(objectFunction) {
    actualObject = objectFunction(actualObject)
    return actualObject
}

-2

「nil」を許可します。

ヒープベースの割り当てに加えて、Javaはnilオブジェクトの使用を受け入れました。単なるアーティファクトとしてではなく、機能として。オブジェクトの状態がnilの場合、宣言を初期化から分離する必要があります。したがって、新しいキーワード。

C ++では次のような行

Person me;

すぐにデフォルトのコンストラクタを呼び出します。


4
うーん、何が間違っているPerson me = Nilか、持っPerson meデフォルトでnilにして使用してPerson me = Person()nil以外のために?私のポイントは、...無記号は、この決定の要因だったとは思えないということである
アンドレス・F.

あなたにはポイントがあります。Person me = Person(); 同様に機能します。
クリスヴァンバエル

@AndresF。またはPerson me、nilでPerson me()デフォルトのコンストラクターを呼び出すと、さらに簡単になります。したがって、何かを呼び出すためには常に括弧が必要であり、そのため1つのC ++の不規則性を取り除きます。
maaartinus 14

-2

Pythonがそれを必要としない理由は、その型システムのためです。基本的に、foo = bar()Pythonで見ると、bar()呼び出されているメソッド、インスタンス化されているクラス、またはファンクタオブジェクトのどれであるかは関係ありません。Pythonでは、ファンクターと関数の間に根本的な違いはありません(メソッドはオブジェクトであるため)。インスタンス化されているクラスはファンクターの特殊なケースであると主張することさえできます。したがって、何が起こっているのかという点で人為的な違いを生み出す理由はありません(特にPythonではメモリ管理が非常に隠されているため)。

型付けシステムが異なる言語、またはメソッドとクラスがオブジェクトではない言語では、オブジェクトの作成と関数またはファンクターの呼び出しとの間に概念的な違いが生じる可能性があります。したがって、コンパイラー/インタープリターがnewキーワードを必要としない場合でも、Pythonのすべての境界を壊していない言語で維持される可能性があることは理解できます。


-4

実際、Javaでは、文字列を使用するときに違いがあります。

String myString = "myString";

とは異なります

String myString = new String("myString");

文字列"myString"が以前に使用されたことがないと仮定すると、最初のステートメントは文字列プール(ヒープの特別な領域)に単一のStringオブジェクトを作成し、2番目のステートメントはオブジェクトの通常のヒープ領域と文字列プールの2つのオブジェクトを作成します。この特定のシナリオでは2番目のステートメントは役に立たないが、言語で許容されることは明らかです。

つまり、newは、通常のオブジェクトのインスタンス化のために割り当てられたヒープの特別な領域にオブジェクトが作成されることを保証しますが、それなしでオブジェクトをインスタンス化する他の方法があるかもしれません。上記は一例であり、拡張性のための余地があります。


2
しかし、これは問題ではありませんでした。質問は「なぜString myString = String("myString");?」(newキーワードがないことに注意してください)
アンドレスF.

@AndresF。一種の明らかな意図... StringクラスのStringを返すStringという名前のメソッドを使用することは正当であり、それを呼び出すこととコンストラクターを呼び出すことの間に違いがなければなりません。何かclass MyClass { public MyClass() {} public MyClass MyClass() {return null;} public void doSomething { MyClass myClass = MyClass();}} //which is it, constructor or method?
-m3th0dman

3
しかし、それは明らかではありません。まず、名前付きの通常のメソッドStringはJavaスタイルの規則に反するため、大文字で始まるメソッドはすべてコンストラクターであると想定できます。次に、Javaに制約されていない場合、Scalaには「ケースクラス」があり、コンストラクターはまさに私が言ったように見えます。それで、問題はどこにありますか?
アンドレスF.

2
申し訳ありませんが、あなたの主張には従いません。あなたは効果的に主張されている構文を意味論ではなく、とにかく質問はについてだったnewためにマネージ言語。私は、それnewがまったく必要ないことを示しました(たとえば、Scalaはcaseクラスには必要ありません)、また、あいまいさもありません(とにかくMyClassclassで名前が付けられたメソッドを絶対に持てないためMyClass)。ありませんあいまいについてはString s = String("hello")、それはコンストラクター以外にはなりません。そして最後に、私は同意しません。構文を強化し、明確にするためにコード規約があります。
アンドレスF.

1
それがあなたの議論ですか?「Java設計者はnewキーワードを必要としていました。そうでなければ、Javaの構文は異なっていたでしょう」私はあなたがその議論の弱点を見ることができると確信しています;)そして、はい、あなたは意味論ではなく構文について議論し続けます。また、何との後方互換性がありますか?Javaなどの新しい言語を設計している場合、CおよびC ++とはすでに互換性がありません!
アンドレスF.

-8

C#では、スタック上に作成されたオブジェクトとヒープを区別するためにnewが重要です。多くの場合、パフォーマンスを向上させるためにスタック上にオブジェクトを作成します。スタック上のオブジェクトがスコープの外に出ると、スタックが解けるとすぐに消えます。プリミティブのように。ガベージコレクタがそれを追跡する必要はありません。また、ヒープに必要なスペースを割り当てるためのメモリマネージャの余分なオーバーヘッドもありません。


5
残念ながら、C#とC ++を混同しています。
gbjbaanb
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.