1

我正在将一个项目迁移到 c++,因为我在用 c# 开发它时遇到了性能上限。然而,这是我第一次使用 c++,我发现自己做了很多看起来不太正确的事情......

考虑以下抽象示例:

class ClassC
{
    ClassC::ClassC(int option)
    {
        //do something
    }
}

class ClassB
{
    ClassC* objC

    ClassB::ClassB(ClassC* objC)
    {
        this->objC = new ClassC(*objC);
    }
}

class ClassA
{
    void functionA(void)
    {
        ClassB objB (&ClassC(2));
    }
}

ClassA 有一个创建 ClassB 的函数。ClassB 的构造函数接受一个 ClassC,objC。objC 是通过引用传递的,因为 ClassC 不是原始类型,而是通过引用存储的,因为 ClassC 没有默认构造函数。但是,由于objC是在静态内存中创建的,并且会在functionA完成时被销毁,所以ClassB需要将objC指向的值复制到动态内存中,然后存储一个指向该副本的指针。

这对我来说似乎很绕,让我觉得我在错误地接近某些东西。这是在 C++ 中做的标准事情吗?

编辑:每个人似乎都在说该行ClassB objB (&ClassC(2));不正确,因为 ClassC 对象的值将在 ClassB 复制它之前丢失。但是我已经编译了我的示例,但事实并非如此。这是修改后的工作代码:

class ClassC
{
    int option;

public:
    ClassC::ClassC(int option)
    {
        this->option = option;
    }

    int ClassC::getOption(void)
    {
        return option;
    }
};

class ClassB
{
    ClassC* objC;

public:
    ClassB::ClassB(ClassC* objC)
    {
        this->objC = new ClassC(*objC);
    }

    int ClassB::getOption(void)
    {
        return objC->getOption();
    }
};

class ClassA
{
public:
    static ClassB functionA(void)
    {
        return ClassB (&ClassC(2));
    }
};

int main(void)
{
    ClassB objB = ClassA::functionA();

    int test = objB.getOption(); //test = 2, therefore objC was copied successfully.

    return 0;
}
4

4 回答 4

4

不确定您真正的问题是什么,但您的代码似乎很脆弱。我想重写你的代码,正如你所展示的那样:

class ClassC
{
    explicit ClassC(int option)
//  ^^^^^^^^ stop implicit conversion, if constructor takes one parameter
    {
        //do something
    }
};

class ClassB
{
    ClassC objC;                         // store by value instead of pointer. 
                                         // Even smart pointer will be better option than raw pointer

    explicit ClassB(const ClassC& objC)  // pass by const reference instead
    : objC(objC)                         // use member initializer list to initialize members
    {
    }
};

class ClassA
{
    void functionA(void)
    {
        ClassB objB(ClassC(2));
    }
};
于 2013-10-06T04:14:01.013 回答
2

获取临时地址并将其保存以备后用是一个很大的禁忌。

ClassB objB (&ClassC(2));  // taking address of temporary

此外,即使在函数参数列表中传递临时的 const 引用也不会比函数调用进一步延长生命周期。即,一旦构造函数完成触发,引用就是 toast,所以:

class ClassB
{
    const ClassC& objC;

public:
    ClassB(const ClassC& objC) : objC(objC)
    {
    }
};

也不会工作。有关原因的详细信息,请在此处阅读更多信息

如果您这样做,它将起作用:

ClassC objC;
ClassB objB(objC);

但话又说回来,你的原始样本也是如此。

保证外部对象生命周期的一种方法是通过智能指针所有权动态分配对象。考虑一下:

class ClassB
{
    std::shared_ptr<ClassC> ptrC;

public:

    ClassB(std::shared_ptr<ClassC> ptrC)
        : ptrC(ptrC)
    {
        // access the instance with ptrC->member()
    }
};

现在你可以这样做:

ClassB objB(std::make_shared<ClassC>(2));

即使objB是值复制(例如在容器上的排序操作等),共享实例仍然完好无损。最后一个走出门的人关掉了灯(在这种情况下,删除了共享ClassC对象)。

显然,对于仅由单个父级持有的单个实例而言,这毫无意义。在这种情况下,我完全同意其他强烈建议您使用移动语义的答案。但是,如果您确实需要共享资源,这是考虑这样做的一种方法。


编辑将传递构造函数添加到 ClassB 作为一个简单的例子。

我刚刚意识到每个人都非常热衷于帮助您构建您的 ClassC 对象,也许您所需要的只是一种objC为构建提供参数的方法。即,也许您完全打算objB完全拥有自己的私有实例,objC而您所需要的只是一种获取参数以进行初始化的方法。

这就是构造构造器初始化列表的用途。请参阅下面的代码,它(根据您的评论,可能对您有用并且容易理解。

class ClassB
{
    ClassC objC;

public:
    // default constructor. initializes objC with default value
    ClassB() : objC(0)
    {
    }

    // explicit pass-through of params to `objC` construction
    explicit ClassB(int option) : objC(option)
    {
    }
};

这使您的代码变得ClassA简单:

ClassB objB(2);

这将调用ClassB::ClassB(int),将提供的参数传递给类型的内部objC对象实例的构造ClassC

于 2013-10-06T04:27:04.980 回答
1

您的构造函数ClassC无关紧要,因为将调用的是复制构造函数

class ClassC
{
    ClassC(int option) // defines a constructor that takes an int
    {
        //do something
    }
}

class ClassB
{
    ClassC* objC

    ClassB(ClassC* objC)
    {
        this->objC = new ClassC(*objC); // dereferences objC calling ClassC::ClassC(const Class& obj) - the default copy constructor.
    }
}

class ClassA
{
    void functionA(void)
    {
        ClassB objB (&ClassC(2)); // passing a reference to a temporary ... bad idea, but since it is copied in ClassB (the object, not the pointer), it will appear okay - if your compiler lets this compile (newer ones should/will likely throw an error "cannot take address of rvalue temporary")
    }
}

总而言之,如果有很多已经提到的建议,这段代码会更好,但值得注意的是,复制构造函数 forClassC是在ClassB.

于 2013-10-06T04:39:03.473 回答
0
ClassB objB (&ClassC(2));

这是一件坏事。您正在获取临时对象的地址,该地址将在此行之后消失。如前所述,尝试使用引用而不是指针。大多数时候,您可以用引用替换指针,这是一种非常安全的方法(它永远不会是“NULL”)。

你可以写

class ClassB
{
    ClassC& objC;

    ClassB::ClassB(const ClassC& objC) :
        objC(objC)
    {
    }
}
于 2013-10-06T04:05:49.720 回答