7

私はこのトピックをインターネットでたくさん検索してきましたが、確固たる答えには至っていません。私は主にC#プログラマーとして、大規模なスコープで、通常はファイルの先頭近くで、関数の外部でクラスを宣言し、使用時にクラスを構築することに慣れています。

C ++に移行した後、これを複製する唯一の方法は、デフォルトのコンストラクターを使用することです。これは問題ありませんが、場合によっては、引数のないデフォルトのコンストラクターよりも引数を必要とするコンストラクターが必要です。

インターネットで解決策を探した後、いくつかのアドバイスに出くわしましたが、それらには欠点があります。

1.ポインタ

一部の人々は、目的のスコープに動的ポインターを設定し、構築時にクラスの場所を指すようにポインターを割り当てることを提案しています。

CClass* pClass = 0;

int main()
{
    pClass = new CClass(1337);

    delete pClass;
    return 0;
}

このアプローチの問題は、後でポインターを削除することを忘れないようにする必要があることです。したがって、静的ポインターの方がはるかに「安全」です。また、ポインタがあるため、それほどではありませんが、これを行うにはわずかなメモリオーバーヘッドがあると思います。

2.とにかくデフォルトの構成を持っている

とにかくデフォルトのコンストラクターを使用することをお勧めする場合があります。これにより、クラス内のすべてがゼロになります。

class CClass
{
public:
    CClass() : leetNumber(0) {}
    CClass(int leetNumber) : leetNumber(leetNumber) {}
private:
    int leetNumber;
};

//Defaults leetNumber to 0 through default ctor
CClass myClass;

int main()
{
    myClass = CClass(1337);

    return 0;
}

しかし、クラス内のすべてをゼロにすることができない場合はどうなりますか?何にも初期化できない別のクラスがそこにある場合はどうなりますか?メンバーを適切に初期化せずに、ユーザーがクラス内の関数にアクセスしようとした場合はどうしますか?(これを確認することはできますが、特にメンバーが多い場合は、コードが多すぎると思います)。

3.より小さく、よりローカルなスコープにとどまる

人々が小さな範囲にとどまり、それを必要とする可能性のある他の関数への参照としてクラスを渡し、クラスを宣言するとすぐに構築すると言われている提案があります。

class CClass
{
public:
    CClass(int leetNumber) : leetNumber(leetNumber) {}
    int getLeetNumber() { return leetNumber; }
private:
    int leetNumber;
};

bool GetMuchNeededAmazingNumberFromClass(CClass& myClass)
{
    if(myClass.getLeetNumber() == 1337)
        return true;

    return false;
}

int main()
{
    CClass myClass = CClass(1337);
    if(!GetMuchNeededAmazingNumberFromClass(&myClass);
        return 1;

    return 0;
}

これは、どの関数が何を必要としているかがわかるという意味では良いのですが、必要な引数が大量にある多くの外部クラスを必要とする関数を想像することができます。

他にも多くの例がありますが、信頼できる例を見つけることができないようです。特に、このようなものが素晴らしくて簡単なC#のバックグラウンドから来ています。

ありがとう。

編集:

私が求めていることについて詳しく説明しましょう。C#では、次のことができます。

public class Program
{
    //See how I'm able to do this, without calling the ctor.
    static AmazingClass amazing;

    public static void Main()
    {
        //And then call the constructor when I want.
        amazing = new AmazingClass(1337);
    }
}

これにより、実際にクラスを作成せずにクラスを作成できます。これがC++で探しているものです。

再度、感謝します。

4

6 に答える 6

6

これは非常に悪い習慣です(クラスをオブジェクトに置き換えました):

私は、大きなスコープで、通常はファイルの先頭近くで、関数の外部でオブジェクトを宣言し、使用時にオブジェクトを構築することに慣れています。

気にしないで。必要なときにオブジェクトを定義します。

int main() { 
     A a;
     ...a...
     A b;
     ...b...
 }

これはC++の考え方です。

私はC#を信じていますが、それも悪い習慣です。オブジェクトを定義せずに使用すると(null参照例外が発生します)、なぜこのような危険なもので遊ぶのでしょうか。

ところで、C#オブジェクトと同等のC ++はshared_ptrです:

std::shared_ptr<A> a;
int main() {
   a = std::make_shared<A>(...);
   // do not need to call delete
}

C ++ではstd::unique_ptr、オブジェクトを共有する必要がない場合にも使用できます。

ただし、これを行わないでください。グローバル変数を使用しないでください...

于 2012-10-15T21:00:42.723 に答える
4

これは、C#では、クラスがヒープベースの参照オブジェクトであるためです。それで:

C#

MyClass a; //null reference
a = new MyClass (param1, param2);

でも:

C ++

MyClass a; //Myclass is constructed on stack

C#バージョンは、C++ではこれと同等です。

Myclass* a = 0; //null reference
a = new Myclass (param1, param2);

クラスはC++ではスタックに存在できますが、C#では存在できません。

C#は、スタック上に存在できる構造体値タイプを提供します。

MyStruct a; //lives on the stack

ただし、引数を使用して構造体コンストラクターを提供できます。

struct MyStruct
{
   public MyStruct (int a, int b) { /*assign to members vars*/ }

   int A, B;
}

C#コード:

MyStruct a; // this creates a on stack and zero initializes int A and B

これ:

MyStruct a; //does same as above

a = new Mystruct(1, 2); //a.A = 1, a.B = 2
于 2012-10-15T21:06:17.800 に答える
3
  1. より小さく、よりローカルなスコープにとどまる

これは、C ++だけでなく、すべての言語で可能な限り実行する必要があることです。

しかし、必要な引数が大量にある多くの外部クラスを必要とする関数を想像することができます。

関数は、取る必要のある引数を取る必要があります。多すぎると感じた場合は、関数を他の部分にリファクタリングする必要があります。引数の数は、関数の複雑さのもう1つの尺度にすぎません。グローバルオブジェクトにアクセスすると、機能が単純化されるだけでなく、グローバルオブジェクトが使用/アクセス/変更された場所を特定するのがはるかに難しくなり、コードの保守がはるかに複雑になります。

関数が多くの引数を取る場合、それは実際には関数ではなく、さまざまな操作の複雑な混乱であるか、そうでなければ引数はおそらくいくつかの意味のあるエンティティにグループ化されます。後者の場合、それらのエンティティを表す型を作成するだけで、いくつかの引数を渡すことになります。

ちなみに、C#で実際に実行していると言っていることを実行できるかどうかはわかりません...特に、C#のほとんどのコードはクラス内にあるため、おそらく、メンバーが宣言されたクラスを実行することに慣れています。上部にあり、他の場所で使用されています。その場合は、C++で同じパラダイムを適用できます。必要なメンバー数でクラスを作成します。

于 2012-10-15T21:04:44.780 に答える
1

コンストラクターをオーバーロードしたいようです。

于 2012-10-15T20:55:12.823 に答える
1

関数で静的変数を使用して、必要になるまでオブジェクトの作成を遅らせます。

Foo & getInstance () {
    static Foo foo(arg1, arg2, ...);
    return foo;
}

void main () {
    Foo & x = getInstance();
}

getInstance(のように)動的オブジェクトを作成する必要があるgetInstance(x, y, z)が、引数を1回だけ渡したい場合は、次のことができます。

struct FooFactory {
    int arg1;
    float arg2;
    Bar arg3;

    bool valid;

    FooFactory ();

    Foo & getInstance ();
};

FooFactory::FooFactory () : valid(false) {}

Foo & FooFactory::getInstance () {
    if (!valid) throw Error();
    static Foo foo(arg1, arg2, arg3);
    return foo;
}

FooFactor factory;

void main () {
    factory.arg1 = ...;
    factory.arg2 = ...;
    factory.arg3 = ...;
    factory.valid = true;
    Foo & x = factory.getInstance();
}

もちろん、これが基本です。私は情報隠蔽などに悩まされていません。また、の代わりにをfactory.getInstance()使用して名前を変更することで回避できます。私もこれが良い考えだと言っているわけではありません。OPの質問を文字通り行う方法を示しています。operator() ()getInstance ()factorygetInstance

于 2012-10-15T20:58:05.237 に答える
1
//See how I'm able to do this, without calling the ctor.
static AmazingClass amazing;

//And then call the constructor when I want.
amazing = new AmazingClass(1337);

すべて(まあ、ほとんどすべて)はC#のリファレンスです。その変数amazingは、を介して割り当てるまで何も参照しませんnew。これがC++に相当するものです:

//See how I'm able to do this, without calling the ctor.
static AmazingClass * amazing;

//And then call the constructor when I want.
amazing = new AmazingClass(1337);

(ほとんど)すべてがC#とJavaの参照であるため、これらの言語のすべてを参照する必要がnewありますnewnewすべてをポインタにすることでC++でも同じ動作を得ることができますが、これを行うことは推奨されるメカニズムではありません。C++には自動化されたガベージコレクションがありません。newC ++で割り当てたものはすべて、最終的にはを介して削除する必要がありdeleteます。C ++で推奨されるメカニズムは、最小限に抑えることですnewdelete

C ++のnew/問題を回避する1つの方法は、をバイパスすることです。目的のタイプの変数を宣言するだけです。これにより、JavaやC#ではできないことができます。型の変数を宣言することはできますが、JavaとC#ではオブジェクト自体を表示することはできません。これらの言語のオブジェクトは、常に参照の背後に隠されています。deletenew

C ++のnew/問題を回避する別の方法は、 RAIIを使用することです。andコマンドdeleteはクラスメソッド内に隠されており、デストラクタは常にそれ自体の後でクリーンアップします。クラスは汚い仕事をします。外の世界では、クラスを使用するだけです。newdelete

C#とC++は異なる言語です。2つの言語を適切に使用するには、異なる考え方を採用する必要があります。

于 2012-10-15T21:31:35.170 に答える