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

This is very bad habit (replaced classes with objects):

I'm used to declaring objects in a large scope, typically near the top of the file, outside any functions, and then constructing them when in use.

Forget about it. Define object when you need it.

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

This is C++ thinking.

I believe in C# it is bad habit too. What if you use object without defining it - you'll get null reference exception - why play with such dangerous thing.

BTW, C++ equivalent to C# object is shared_ptr:

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

In C++ you can also use std::unique_ptr if you do not need an object to be shared.

But don't do this, don't use global variables...

于 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++ 中,而且在所有语言中都应尽可能多地使用。

但我可以想象一个函数需要大量具有大量所需参数的外部类。

函数应该接受他们需要接受的参数。如果您觉得有太多,也许您应该将功能重构为其他部分。参数的数量只是衡量函数复杂性的另一种方式。访问全局对象不仅不会简化函数,还会使识别全局对象在哪里使用/访问/修改变得更加困难,进而使您的代码维护起来更加复杂。

如果你的函数有很多参数,要么它不是一个真正的函数,而是不同操作的复杂混乱,要么参数可能组合成一些有意义的实体。在后一种情况下,只需创建代表这些实体的类型,您最终将传递一些参数。

附带说明一下,我不确定在 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)),但只想传入一次参数,您可以这样做:

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()通过使用operator() ()而不是getInstance ()和重命名factory来避免getInstance。我也不是说这是个好主意。我只是在展示如何从字面上完成 OP 的问题。

于 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++ 没有自动垃圾收集。您在 C++ 中分配的所有内容new最终都必须通过delete. C++ 中的首选机制是保持newdelete降低到最低限度。

new在 C++ 中解决/问题的一种方法delete是绕过new. 只需声明所需类型的变量。这为您提供了一些您在 Java 和 C# 中无法做到的事情。您可以声明一个类型的变量,但 Java 和 C# 不允许您看到对象本身。这些语言中的对象总是隐藏在引用后面。

new在 C++ 中解决/问题的另一种方法delete是使用 RAII。newand命令隐藏在delete类方法中,析构函数总是自行清理。班级做肮脏的工作。对于外部世界,您只需使用该类。

C# 和 C++ 是不同的语言。您必须采用不同的思维方式才能正确使用两种语言。

于 2012-10-15T21:31:35.170 回答