【问题标题】:C++ syntax of constructors " 'Object1 a (1, Object1(2))''构造函数的 C++ 语法“'Object1 a (1, Object1(2))''
【发布时间】:2010-03-27 05:04:54
【问题描述】:

我在程序中有这样的语法

/* The Object1 is allowed to be changed */
class Object1 : BaseClass {
    BaseClass *link;
    int i;
    public: 
    Object1(int a){i=a;}
    Object1(int a, Object1 /*place1*/ o) {i=a; link= &o;}
};

int main(){
/* The initialization syntax must be preserved. No any new(), no other local objects b,c */
    Object1 a(1, /*place2*/ Object1(2));
    ...
}

我需要什么?我想在第一个对象中保存指向第二个对象的链接(指针)。我应该在原地使用引用“&”吗?

什么类型的地方会有“Object1(2)”?它是匿名对象的构造函数吗?它会有“自动”存储类型吗?

谢谢

更新:

在place2中,语法是固定的,我真的必须支持创建“链”,比如

    Object1 a(1, Object1(2, Object1(6, Object1(999753))));

我无法在 a 的定义中添加任何符号。

更新2: 对于 place1:Object1(int a, Object1 &o) {i=a; link= &o;}Object1 a(1, Object1(2)); 在 place2 我有一个编译错误(g++):

main.cpp||In function `int main()':|
main.cpp|19|error: no matching function for call to `Object1::Object1(int, Object1)'|
main.cpp|9|note: candidates are: Object1::Object1(const Object1&)|
main.cpp|14|note:                 Object1::Object1(int, Object1&)|
main.cpp|13|note:                 Object1::Object1(int)|

【问题讨论】:

  • 你的设计不行。在a 创建的那一刻,所有这些临时人员都会死去,而你没有任何工作可做。
  • 我可以更改类 Object1,定义复制构造函数,将临时重新分配到堆并将 ptr 存储到堆。
  • 是的,存储所提供对象的副本是一个更好的主意。
  • 您不能将临时引用作为非常量引用。
  • @Dennis :我不明白他如何存储副本,除非它是基类的副本,在这种情况下,他会他的论点。

标签: c++ syntax


【解决方案1】:

在“place1”中,您需要一个参考。 Object1 没有完全定义,所以你不能按价值接受。也就是说,您不想按值传递;当您获取它的地址时,您将获得副本的地址,而不是实际对象。

由于您只想要一个指向 BaseClass 的指针,因此只对参数设置该限制可能更有意义。 (当然,如果您真的需要Object1,请在link 的类型中反映:Object1* link;)。

还可以考虑使用初始化列表:

Object1(int a) : i(a), link(0) /* <- important! */ { }
Object1(int a, BaseClass& o) : i(a), link(&o) { }

始终使用初始化列表。

"place2" 使用构造函数创建Object1 的未命名实例。它有 no storage(?) auto storage 和 dies at the end of the expression

也就是说,一旦你点击了Object1 a(1, /*place2*/ Object1(2)); 中的分号,它就不再存在,并且你有一个指向不存在对象的指针!这会导致未定义的行为。

给它一个超越表达式的生命:

Object1 x(2);    
Object1 a(1, x);

始终确保当您引用一个对象时,它会在您引用它的整个过程中一直存在。


您的新编译错误是因为您无法引用临时变量。不过,您可以拥有一个带有 const 引用的。问题是,你的指针现在需要指向 const BaseClass,这可能会影响你想要的。

再次遗憾的是,您的设计需要重新设计。

【讨论】:

  • 我可以将 place1 之前的代码更改为 BaseClass。对我有帮助吗?
  • @osgx:这也可以,而且可能是首选。因为您只需要一个指向BaseClass 的指针,所以不妨引用一个。 (不是按价值!你会切的。en.wikipedia.org/wiki/Object_slicing
  • 无论你决定使用什么类型,都不能通过值传递,因为那样你会存储副本的地址,这肯定不是我们想要的。
  • @Dennis:当然,谢谢。完全错过了。 @Everyone:临时有auto 存储还是没有存储?编辑:似乎是auto,所有对象都有存储空间。
  • 我有一个编译错误,首先使用了引用 char。见更新2
【解决方案2】:

位置 1:在此处使用指针 (*) 而不是引用 (&)。指针语法更明确地指示链接。

地点 2:这里将创建所谓的“临时”对象。是的,它将涉及对 Object1 的构造函数的调用。另外,是的,存储将是“自动”的,因为它将在堆栈上创建。

不过要小心。在您可以在构造函数中引用它之前,临时对象将消失。这肯定会使您的程序崩溃。

如果你必须传递一个临时的,你可以通过在堆上创建一个副本来成功。像这样的:

class Object1
{
    BaseClass* link;
    int i;
    Object1(int a) : i(a), link(NULL) {}
    Object1(int a, const Object1& o) : i(a), link(new new Object1(o)) {} 
    Object1(const Object1& other) i(other.i) link(other.link) {} //Copy constructor
    virtual ~Object1() { delete link; } //Don't forget the 'delete' in destructor.
};

然后在你的 main 你可以()这样做:

int main() 
{
    Object1 a(3, Object1(2));
    ....
}

请注意 Object1 类中的析构函数。确保在其中的链接上调用删除运算符。然而,这样的调用会导致删除链中的所有子节点。如果这不是您想要的,请相应地更改您的删除机制。无论如何,不​​要忘记删除。

另外,确保析构函数是虚拟的。那是因为您将在指向派生类对象的基类指针上调用 delete。

【讨论】:

  • 这个临时对象是按值、按引用还是按指针返回(从构造函数)?
  • 构造函数不会像其他函数一样返回任何东西。但是,要回答您的实际问题,作为构造函数调用的结果,您将以实际对象结束,而不是引用或指针。因此,您必须使用地址运算符来获取其地址。然而,正如其他人所警告的那样,这个“临时”在创建后几乎会立即死亡。所以你一定会得到一个错误的指针。相反,您应该创建一个局部变量并在此处传递该变量的地址。
  • 是的,有了更多的局部变量,我的代码可以正常工作。但我必须支持这种链接语法。
  • 这个临时构造函数只是一个表达式。任何表达式都有一个完整表达式的类型。所以我称之为“将返回 smth 的构造函数调用”
  • 顺便说一句,如果你必须链接,并且仍然想支持没有新运算符 (Object1 a(... Object1(2))) 等的语法,那么你应该这样做:里面Object1 的构造函数将传递的临时对象复制到堆上创建的另一个对象中。临时文件仍然会死掉,但你会随身携带一份有效的副本。
【解决方案3】:

您不能在声明中使用Object1 的实例(它尚未声明)。您可以在类声明之前添加称为前向声明的内容,如下所示:

// Forward declaration
class Object1;

// Now the class body
class Object1 {
    // some reference to Object1 here
};

但是,我仍然不完全清楚您要使用此代码做什么。了解更多关于您希望此构造函数做什么的信息可能会有所帮助?我怀疑您可能希望使用指针或按引用传递,而不是按值传递。

【讨论】:

  • 我想构造树(在实际代码中)或列表(在这个示例代码中)的形式(这种形式是固定的,我不能使用任何其他形式。我应该写一个类,它可以用这样的代码初始化)Object1 an_object (1,Object1(2,Object1(3,Object1(10,Object1(0)))))。但我不明白,这里的“匿名构造函数”是什么?此类声明中“Object1(0)”的类型和含义是什么。谢谢。
【解决方案4】:

你想做什么是不可能的,因为你已经写好了。 /*place2*/ 处的 Object1 的生命周期在构造函数完成后结束,因此您将拥有一个悬空指针。

一般来说,您希望/*place2*/ 处的Object1 成为引用,或直接作为指针[在这种情况下,您不会获取地址,直接复制它]。

但是,您必须非常小心您存储的指针的生命周期,正如初始点所证明的那样。

【讨论】:

    【解决方案5】:

    构造函数参数o 是您传入的Object1 实例的副本(因为它是按值传递的。在您的构造函数中,当您设置你的成员指针指向它的地址,那个地址就是栈帧,在你的构造函数返回后,这个地址就失效了。

    这是另一种方法(当然还有其他方法):

    class Object1 : BaseClass
    {
        BaseClass *link;
        int i;
    
    public: 
        // - Prefer initialization to assignment
        // - Initialize ALL of your members
        Object1(int a) : link(NULL), i(a) {}
    
        // This instance assumes ownership of p
        Object1(int a, Object1 *p) : link(p), i(a) {}
    
        ~Object1() { delete link; }
    };
    
    int main()
    {
        Object1 a(1, new Object1(2));
        ...
    }
    

    编辑:嗯,我能看到你使用你拥有的链接设计的唯一方法是这样丑陋的:

    class Object1 : BaseClass
    {
        BaseClass *link;
        int i;
    
    public:
        Object1(int a) : link(NULL), i(a) {}
    
        Object1(int a, const Object1& o) : link(new Object1(o)), i(a) {}
    
        // Copy constructor
        Object1(const Object1& other) : link(NULL), i(other.a)
        {
            // Deep copy linked object
            if (other.link != NULL)
            {
                link = new Object1(*other.link);
            }
        }
    
        // Assignment operator that does deep copy
        const Object1& operator=(const Object1& that)
        {
            // Check for assignment to self
            if (this != &that)
            {
                delete this.link; 
                this.link = NULL;
    
                // Deep copy linked object
                if (that.link != NULL)
                {
                    this.link = new Object1(*that.link);
                }
    
                this.i = that.i;
            }
        }
    
        ~Object1() { delete link; }
    };
    
    int main()
    {
        Object1 a(1, new Object1(2));
        ...
    }
    

    【讨论】:

    • 对不起,这样可以,但我必须支持原始语法Object1 a(1, Object1(2));
    【解决方案6】:

    您要实现的是堆栈上的动态数组。您想创建一个对象列表并且不将它们保留在堆中。 C++ 不支持这一点,至少不在一个函数的范围内。

    你可以用递归重构你的代码,并保持一个链表在堆栈上,但这不会是非常 C++ 的。

    否则,唯一的解决方案是使用堆(std::liststd::auto_ptrstd::unique_ptr 应该可以解决问题),正如其他人所建议的那样。

    【讨论】:

    • 我需要支持place2中给出的语法。实际上,在我的任务中没有列表,而是一棵树。该列表只是一个小例子。我有非常具体的问题,不需要深度重构。谢谢
    猜你喜欢
    • 1970-01-01
    • 2013-10-14
    • 2014-11-13
    • 2015-01-26
    • 2014-05-29
    • 1970-01-01
    • 2013-08-15
    • 2018-01-06
    • 2018-01-21
    相关资源
    最近更新 更多