在不构造C ++的情况下,在C ++中声明实例变量的好方法是什么?

时间:2012-10-15 20:48:57

标签: c++ class scope

我一直在互联网上搜索这个话题,我还没有真正得到一个坚定的答案。作为一个C#程序员,我习惯于在大范围内声明类,通常在文件顶部附近,在任何函数之外,然后在使用时构造它们。

继续使用C ++之后,复制它的唯一方法是使用默认构造函数,这很好,但在某些情况下,我宁愿使用一个构造函数,它需要参数而不是一个无参数的默认构造函数。 / p>

在互联网上寻找解决方案后,我发现了一些有缺陷的建议:

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 ++中寻找的。

再次感谢。

6 个答案:

答案 0 :(得分:6)

这是一个非常糟糕的习惯(用对象替换了类):

  

我习惯于在大型中声明对象   范围,通常靠近文件的顶部,在任何函数之外,和   然后在使用时构建它们。

忘了它。在需要时定义对象。

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

这是C ++的思考。

我相信C#也是坏习惯。如果您在没有定义对象的情况下使用对象会怎么样 - 您将获得空引用异常 - 为什么要玩这样危险的东西。

BTW,相当于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

但是不要这样做,不要使用全局变量......

答案 1 :(得分: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

答案 2 :(得分:2)

  
      
  1. 保持更小,更本地化的范围
  2.   

这是你应该做的,不仅在C ++中,而且在所有语言中都是如此。

  

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

函数应该采用他们需要的参数。如果你觉得有太多,也许你应该将功能重构为其他部分。参数的数量只是衡量函数复杂性的另一个指标。访问全局对象不仅不会简化功能,而且会使识别全局对象的使用/访问/修改位置变得更加困难,从而使代码的维护变得更加复杂。

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

另一方面,我不确定在C#中你真的做了你说你正在做的事情......特别是,C#中的大多数代码都是在类中,所以你习惯做的很可能就是上课成员在顶部声明并在其他地方使用。如果是这种情况,您可以在C ++中应用相同的范例。创建类,包含您需要的成员数。

答案 3 :(得分:1)

听起来你想要重载构造函数。

答案 4 :(得分:1)

在函数中使用静态变量来延迟对象的创建,直到需要为止。

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

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

如果您需要getInstance来创建动态对象(如getInstance(x, y, z)中所述),但只想传递一次aguments,则可以执行以下操作:

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的问题。

答案 5 :(得分: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降至最低。

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

C ++中new / delete问题的另一种解决方法是使用RAII。 newdelete命令隐藏在类方法中,析构函数总是在自身之后清理。班上做了肮脏的工作。对外界你只需要上课。

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