如何在不调用非const方法的情况下更改私有成员?

时间:2012-01-24 14:16:12

标签: c++ dynamic assembly segmentation-fault

我有问题。我花了大约5个小时尝试一切,但我甚至无法正确地重现它,所以我包括简化的原始源代码。我为此感到抱歉,但我想包括我迄今为止发现的所有相关信息。这是我感到完全无能为力并且请求你帮助的少数几次之一。欢迎任何想法。此外,任何评论都可以为此事带来至少一些启示。在这种情况下的行为对我来说是一个完全的谜。

我在Ubuntu的QtCreator中编程。我正在尝试使用候选解决方案群来开发一个解决数学问题的框架,这应该演变成真正的解决方案。

涉及3个班级:人口,人口成员和问题:

class PopulationMember
{
    QVector<Number> x_;
    QVector<Number> y_;
    Population* population_;   EDIT: Sorry, this is a pointer
    void evaluate();
    friend class Population;
};

class Population
{p
public:
    QList<PopulationMember*>       members_; // created on the heap
    Problem* problem_;                       // created on the heap
    const Problem* readProblem() const;p
    void evaluate();
    void report() const; 
 ...
};

class Problem
{
public:
    void evaluate(PopulationMember&)const;
};

通常我的程序在循环中运行,其中人口调用其各种方法。其中之一是Population :: evaluate()。我的程序运行得很好,直到我介绍了一些新的人口方法。

for (int i = 1; i != 101; ++i)
{
    Population->evaluate();
    Population->someMethod1();
    Population temp = Population->clone();
    temp->someMethod2();
    Population->append(temp);
    Population->someNewMethod();
    Population->someSorting();
    if (i % 10 == 0)
        Population->report();
}

然后我在程序中间出现分段错误。最奇怪的是它只在10个循环之后,在填充执行报告()之后发生。在经过一些实验之后,当我排除了所有需要从report()方法动态分配某些排序(字符串)的操作时,我没有得到错误。相反,当我禁用排序方法(使用std :: sort或qSort)时,问题就会停止。当我离开临时人口所做的行动时,也没有问题。所以我开始调试程序。我让它完成10个循环并开始逐步调试。我进入了人口 - &gt; evaluate();

void Population::evaluate()
{   
    for (Iterator it = begin(); it != end(); ++it)
    {
        std::cout << problem_;   // debug see bellow:
    (*it) -> evaluate();     // If I change to problem_->evaluate(**it); the program works.
}

}

调试: 打印出的地址是0xbffff628。这与之前的10 *种群_-&gt; members_.count()打印输出相同。

我进去(*它) - &gt;评估();在这里,我切换到汇编代码:

  864           (*it) -> evaluate();
0x805380c  <+122>:  lea    -0x10(%ebp),%eax
0x805380f  <+125>:  mov    %eax,(%esp)
0x8053812  <+128>:  call   0x8055d84 <QList<PopulationMember*>::iterator::operator*() const>
0x8053817  <+133>:  mov    (%eax),%eax
0x8053819  <+135>:  mov    %eax,(%esp)
0x805381c  <+138>:  call   0x805ae08 <PopulationMember::evaluate()>

我进入最后一条指令的函数调用。在我这样做的瞬间,根据我的调试器,problem_中的所有属性都变得无法访问。 在这一点上,所有人都迷失了。

void PopulationMember::evaluate()
{
    population_ -> readProblem() -> evaluate(*this);
}

    135 {
0x805ae08  <+000>:  push   %ebp
0x805ae09  <+001>:  mov    %esp,%ebp
0x805ae0b  <+003>:  sub    $0x18,%esp
        136     population_ -> readProblem() -> evaluate(*this);
0x805ae0e  <+006>:  mov    0x8(%ebp),%eax
0x805ae11  <+009>:  mov    0x4(%eax),%eax
0x805ae14  <+012>:  mov    %eax,(%esp)
0x805ae17  <+015>:  call   0x8051bc4 <Population::readProblem() const>
0x805ae1c  <+020>:  mov    0x8(%ebp),%edx
0x805ae1f  <+023>:  mov    %edx,0x4(%esp)
0x805ae23  <+027>:  mov    %eax,(%esp)
0x805ae26  <+030>:  call   0x804e962 <Problem::evaluate(PopulationMember&) const>
        137 }
0x805ae2b  <+035>:  leave
0x805ae2c  <+036>:  ret
0x805ae2d   nop

const Problem* Population::readProblem() const
{
    std::cout << problem_ << std::endl; // debug see bellow: 
    return problem_;
}

调试: 最后,problem_指向的地址变为0xbffff780而不是0xbffff628。增量为344

这总是发生。增量为344.如果我在程序中做了一些小改动,地址会改变,但这两个地址之间的差异仍然是344.这更令人费解,因为我所有三个类的大小都小于100。 / p>

程序在void Problem :: evaluate(PopulationMember&amp;)const中崩溃;一旦涉及某些逻辑,方法就会立即生效。

编辑:

Population Population::clone()
{
    Population temp(*this);
    return temp;
}

Population::Population(const Population& population)
{
    this->setProblem(population.problem_);

    Population::ConstIterator cit;
    for (cit = population.constBegin(); cit != population.constEnd(); ++cit)
        this->addCopy(*cit);

    this->ownsMembers_ = true;
}

void Population::addCopy (PopulationMember* populationMember)
{
    PopulationMember *temp = new PopulationMember(*populationMember); // Memberwise
    temp -> population_ = this;
    members_.push_back(populationMember);
}

Population::~Population()
{
    if (ownsMembers_)
        foreach (PopulationMember* X, members_)
            delete X;
}

void Population::append(Population& population)
{
    if (population.ownsMembers_)
    {
        members_.append(population.members_);
        population.ownsMembers_ = false;
    }
    else
        members_.append(population.members_);
 }

1 个答案:

答案 0 :(得分:1)

 Population Population::clone()
 {
   Population temp(*this);
   return temp;
 }

您正在复制Population - 个实例:1。您按值返回本地副本,2。通过分配到另一个本地

再次复制
Population temp = Population->clone();

所有这些实例都获得指向PopulationMember的指针,ownsMembers_始终设置为true - 这看起来有点可疑,您可能希望在析构函数/构造函数中使用断点进行调试,以找出生命周期每个人口及其成员。

编辑:追加方法

void Population::append(Population& population)
{
    if (population.ownsMembers_)
    {
        members_.append(population.members_);
        population.ownsMembers_ = false;
    }
    ...

这意味着成员不再指向正确的人口了! Population&的值存储在堆栈中,并在for循环结束后被删除,但PopulationMembers仍然指向这些Populations。

编辑:修复

请试试这个:

void Population::append(Population& population)
{
    if (population.ownsMembers_)
    {
        for (cit = population.constBegin(); cit != population.constEnd(); ++cit)
            (*cit)-> population_ = this;

        population.ownsMembers_ = false;
    }

    members_.append(population.members_);
}