質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.47%

  • C#

    7437questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • C++

    3597questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

C#にコピーコンストラクタがないのはなぜ?

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 5,716

aridai1221

score 31

C#初心者です。
最近疑問に思ったことなのですが、
C#にはなぜコピーコンストラクタが存在しないのでしょうか。

C++では
参照型のデータの整合性を保つため、
メモリの多重解放を防ぐために
コピーコンストラクタで対策をすると思います。
(参考にさせてもらったページ)

C#では
プログラマ自身がメモリの解放処理を書くことは無いのですが、
データの整合性を保つためという点においては
コピーコンストラクタがあれば便利だと思います。

以下は考えるのに使ったコードです。

using System;

public class Program
{
    public static void Main()
    {
        //  構造体(値型)のインスタンスを生成する
        var hoge = new Hoge { foo = new Foo(100) };

        //  このままの状態で値を確認してみる
        Console.WriteLine(hoge.foo.x);

        //  関数に値渡しをしてから
        //  値を変更させてみる
        func(hoge);

        //  値をもう一度確認してみる
        Console.WriteLine(hoge.foo.x);
    }

    public static void func(Hoge hoge)
    {
        //  コピーコンストラクタが呼ばれて
        //  hoge.foo = new foo(); のような処理によって
        //  hoge.fooのメモリを新しく確保させて
        //  渡された元のメンバには
        //  なんの影響も与えさせないようにさせたいな…
        //  でもコピーコンストラクタが言語仕様に存在しないな…

        //  渡された元のメンバと
        //  同じものを参照しているから
        //  ここで値を書き換えると
        //  元の方にも影響が出てしまう…
        hoge.foo.x = 200;
        Console.WriteLine(hoge.foo.x);

        //  ここで仮引数のhogeの寿命は終わってしまう
        //  もしC++なら
        //  newで確保したメモリはdeleteで解放する必要がある
        //  もしデストラクタでメモリの解放処理を行っているのであれば
        //  hogeの寿命が終わるのでメモリの解放処理が呼ばれると思う
        //  もし、hoge.fooのメモリをここで解放してしまうと
        //  元のhoge.fooが指しているものもメモリ解放されてしまう…
        //  幸いこのコードはC#で書かれているため
        //  そんなことは起こらないのだが…
    }
}

//  値型
public struct Hoge
{
    //  参照型のメンバを持つ
    public Foo foo;
}

//  参照型
public class Foo
{
    public int x;
    public Foo(int x) { this.x = x; }
}


出力結果はこうなりました。

100
200
200

やはり渡された元のインスタンスのメンバに
影響を与えているようです。

このようなことを防ぐために
Clone()メソッドのようなものを実装するという方法がありますが、
これは言語仕様で強制的に使わないといけないというものではないため
プログラマが忘れればやはり
あのようなバグの原因になるうるコードになります。

このようなことから
私はC#にコピーコンストラクタが存在していても良いと思ったのですが、
なぜ言語仕様に存在しないのでしょうか。

初心者すぎる質問ですいません…

追記
構造体のメンバにクラスを含める事自体が根本的に間違っているというか
状況としては少ないということに気づきました。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+5

私見ですが、言語設計の思想によるものと思います。

C++の基であるCは原則値渡しであり、全ては値のコピーです。構造体も値渡しであり各メンバーのコピーです。C++のクラスはCの構造体の拡張であるため、基本は値渡しであり、各メンバーのコピーです。しかし、単純なコピーでは不整合が起きる場合があるため、コピーのための処理が追加できるようにコピーコンストラクタが作られました。デフォルトコピーコンストラクタが単純なコピーであるのもその影響です。

しかし、その後のGCがあるオブジェクト指向言語(特にJava)では、オブジェクト全体をコピーをするという処理を言語の基本に置かないようにしました。変数はオブジェクトへの参照にし、参照値(Javaの場合の用語、他の言語では違う用語になるか、実装上はただのポインタの場合が多い)のみをコピーすることを基本にしました。つまり、C++風に言えば、全体をコピーするのではなく、ポインタをコピーすることを基本にしたのです。ポインタのコピーを基本にした場合、いつメモリ解放できるのかの問題が出てきます。これを補うためにGCを必須とし、言語仕様に組み込んだのです。

C#はJavaの言語設計に大きく影響を受けた言語です(ただし、C#の直接の祖先はDelphiです)。クラスが定義するオブジェクトは基本的に参照型とし、同じように参照値のみのコピーとしました。各フィールドをコピーすることは処理が重く、無駄であると判断したのです。ただ、フィールド全ての値をコピーした場合もいい事があるため、Javaより一工夫を入れ、値型として構造体を別途用意しました(C++ではclassとstructは実質同じなのに対し、C#ではclassとstructは大きく異なります)。もし、値を全てコピーするような使い方をしたかったら構造体を使うようにしたのです。しかし、そのような場合は限られていおり、また、実装上の問題もあるため、構造体はクラスに比べて制限を設けるようにしたのです。

なぜ、このような流れになったのでしょうか。理由は単純です。コピーコンストラクタが遅いからです。

コピーコンストラクタはコストが高い処理です。高速なC++であっても巨大なvectorやstringに対してコピーコンストラクタを走らせた日には泣きたくなるぐらい遅いです。C++ができる人ほどコピーコンストラクタを避ける傾向にあります。プリミティブ型などを除き、関数への引数はポインタ値渡しか参照渡しを使います。どうしても値を返さなければならないときは右辺値参照になるよう工夫をしてムーブコンストラクタを使います。C++11以降はスマートポインタやムーブコンストラクタが使いやすくなったため、コピーコンストラクタが使う傾向はますます減ってきていると思います。

最後に、コピーコンストラクタで無ければ呼び出し先で影響があるのでは?という話ですが、それを避けるために、最近の流行が関数型プログラミングを模倣した設計です。どういうことかというと、

  • オブジェクトを変更不可(immutable)にする。
  • 参照透過な関数のみにする。(参照透過性とは副作用が一切無く、引数が同じなら常に同じ値を返すという性質です)

ということです。上を完全に守ろうとするとモナドやカリー化と言った関数型言語特有脳機能がないと厳しいですが、部分的に取り入れることは他のどのような言語でも可能です。変更不可と参照透過性が守られていれば、オブジェクトを参照の値渡しで関数に渡しても勝手に変えられたという問題が無くなります。そのとき、わざわざ処理が重いコピーコンストラクタを使おうと思う人は誰もいなくなると言うことです。メモリ解放問題についてはGC頼りですが、C++でもスマートポインタを使えばある程度緩和はできますので、こちらも問題にする人はいなくなると思います。

※ Cは上の話とは別です。Cはメモリの全ての処理を完全に手動で把握したい人が使う言語です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

C#のクラスは「参照型」と良くいわれますが、実際にはC++の「ポインタ」のように振る舞います。
例えば、C++の構造体にポインタ型のメンバ変数があったとして、その構造体のオブジェクトをコピーすると、当然ポインタもコピーされますが、コピーされたポインタはコピー元のポインタと同じインスタンスを参照していますよね。それと同じ原理です。
したがって、ご質問にあるコードと同様のコードをC++で作ると、結果も同様になります。

データの整合性を保つためという点においては 
コピーコンストラクタがあれば便利だと思います。 

コピーコンストラクタはC#でも作れます。
→ 方法 : コピー コンストラクターを記述する (C# プログラミング ガイド)

ただし、コピーコンストラクタはデータの整合性を保証するためのものではありません。データの整合性を保証することは設計者の責務です。メソッドに渡したオブジェクトが変更されないようにしたいなら、そのようなコードを書かなくてはなりません。もしメソッドに渡したオブジェクトが変更されるなら、その旨をコメントなり仕様書なりに明記しなくてはいけません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

0

クラスはnewをしたときだけインスタンスがつくられるとおぼえておけばいいと思いますよ(たぶん

値型のstructは一時的に使ったりパフォーマンスを求めたり、dllとのやりとり。

structの中にclassを持たせるのはあまり見ないですね。コードの通り、値渡しの時classは参照を渡すだけですので。

何故かというと……なんでだっけかな?^^;
ただ、参考ページのやり方ですと

Kitty obj = g_obj;

これ、実際にKittyクラスのコンストラクタを見に行かないと何をしているかわからないんですよ。

>プログラマが忘れればやはり
>あのようなバグの原因になるうるコードになります。

むしろC++のコピーコンストラクタのほうがバグの原因になりがちかもしれません。C#になれたら違和感なくかけると思われますよー

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/14 20:09

    > structの中にclassを持たせるのはあまり見ないですね。コードの通り、値渡しの時classは参照を渡すだけですので。
    確かに言われてみればそうですね。
    私自身も構造体にクラスのインスタンスを持たせる実装はあまりないです。

    キャンセル

関連した質問

同じタグがついた質問を見る

  • C#

    7437questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • C++

    3597questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。