【问题标题】:Unusual C++ behaviour when calling constructor调用构造函数时的异常 C++ 行为
【发布时间】:2013-11-13 22:19:06
【问题描述】:

为了成为一名更有能力的 C++ 程序员,我正在尝试使用引用。过去,我通常在引用对象时使用指针,例如在 Objective-C 中。

所以我一直在编写一个 多项式 类来存储 Term 对象列表

(TermNode* termHead,termTail)

但是当我尝试使用列出的第一个实现将术语添加到列表时,在添加术语中调用术语上的构造函数时,会覆盖先前创建的术语节点中的 Term& 引用,就好像它使用了 this 上一次调用构造函数的指针。

列出的第一个实现在技术上存在什么问题,导致其行为异常?它只在我使用指针和 new 时起作用,即使我没有更改 TermNode 的结构。

struct TermNode {
    Term& value;
    TermNode* next;
};

Term::Term(int coefficient,int firstTermDegrees,int secondTermDegrees) {
        this->coefficient = coefficient;
        this->xDegree = firstTermDegrees;
        this->yDegree = secondTermDegrees;
    }

//Doesn't work
void Polynomial::addTerm(int coefficient, int xDegree, int yDegree) {
    Term term(coefficient,xDegree,yDegree);
    addTerm(term);
}

void Polynomial::addTerm(Term& term) {
    TermNode* t = new TermNode{term,nullptr};
    if(isEmpty())
    {
        termHead = t;
        termTail = t;
    }
    else
    {
        termTail->next = t;
        termTail = termTail->next;
    }

}

//Does work
void Polynomial::addTerm(int coefficient, int xDegree, int yDegree) {
    Term* term = new Term(coefficient,xDegree,yDegree);
    addTerm(term);
}

void Polynomial::addTerm(Term* term) {
    TermNode* t = new TermNode{*term,nullptr};
    if(isEmpty())
    {
        termHead = t;
        termTail = t;
    }
    else
    {
        termTail->next = t;
        termTail = termTail->next;
    }

}

bool isEmpty() {
return nullptr == termHead;
}

【问题讨论】:

  • 使用引用作为结构/类成员是不常见的,而且非常棘手。你必须绝对确定被引用事物的生命周期长于结构对象的生命周期。

标签: c++ pointers reference constructor pass-by-reference


【解决方案1】:
//Doesn't work
void Polynomial::addTerm(int coefficient, int xDegree, int yDegree)
{
    Term term(coefficient,xDegree,yDegree);//here you created automatic object
    addTerm(term);                         //it will be deleted in next line
}                                    //it is an error to call addTerm(Term& term)

这行得通

//Does work
void Polynomial::addTerm(int coefficient, int xDegree, int yDegree)
{
    Term* term = new Term(coefficient,xDegree,yDegree);
    addTerm(term);
}

因为在这里您在免费商店中创建了对象。它的寿命会延长,直到您在其上调用delete(旁注:在某处调用delete,此时您有内存泄漏!或使用smart pointer),所以这很好用。

void Polynomial::addTerm(Term* term)
{
    TermNode* t = new TermNode{*term,nullptr};
    // ...
}

您可以使用引用,但应以应有的方式使用。当它被实际删除时,您不能使用对临时的引用。但是,您可以将临时对象绑定到 const 引用,但由于您的结构定义

struct TermNode
{
    Term& value;
    TermNode* next;
};

在构造函数中再次获取引用,因此在这种情况下将临时绑定到 const 引用将再次导致分段错误。

【讨论】:

  • 因此,当我退出作用域时,引用不再存在,但指针仍保留在堆中。即使我使用语法 addTerm(Term& t),离开范围也包括跳入一级。因此,在堆上构建列表的唯一方法是传递一堆指针,正如我所期望的那样?
  • 一个指针存储在堆栈上是特定的,但指向的对象存储在堆上。您可以使用引用,但以它们应该使用的方式。当它被实际删除时,您不能使用对临时的引用。您可以将临时对象绑定到 const 引用,但是我将其添加到 anwer
  • 这是一个很好的观点。是的,我的意思是指针是自动的,但离开范围不会释放它指向的内容,因为离开范围会删除引用,即使它是调用而不是返回。对吗?
  • 是的,这是正确的:这不会释放在空闲存储上分配的对象(这会删除对象,但是如果它是建立在堆栈上的,也可以)
【解决方案2】:

在第一个实现中,您传递一个对临时对象的引用,该对象在方法 addTerm 结束时被销毁。

【讨论】:

    猜你喜欢
    • 2023-04-07
    • 2012-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多