c ++ Перезапись уже определенной переменной - PullRequest
4 голосов
/ 27 января 2012

У меня есть следующая основная функция, создающая произведение коэффициентов с помощью указателей.Это лишь небольшая часть проекта, которая используется для создания полиномов:

#include "header.h"
int main()
{
    TermProd x = TermProd( new Coeff(4), new Coeff(8));
    x.print(); cout << endl;
    x = TermProd( new Coeff(8), new Coeff(15));
    x.print(); cout << endl;
    return 0;
}

После тестирования перезапись кажется работающей.Но когда я вызываю печать на x, я получаю ошибку сегментации.Я уже давно пытаюсь и смотрю на это, но не могу понять реальную проблему.Также мои поиски не привели к правильному направлению, поэтому я решил создать небольшой фрагмент кода, который воспроизводит ошибку.

Мой файл header.h выглядит так:

class Term{
public:
    Term(){};
    virtual ~Term(){};
        virtual Term* clone() const = 0;
    virtual void print() const = 0;
};

class Coeff:public Term{
    int m_val; //by def: >=0
public:
    // Constructor
    Coeff();
    Coeff(int val = 1):m_val(val)
    // Copy constructor
    Coeff* clone() const{return new Coeff(this->val());}
    // Destructor
    ~Coeff(){}
    // Accessors
    int val() const{return m_val;} ;
    // Print
    void print() const{cout << m_val; }
};

class TermProd:public Term{
    TermPtr m_lhs, m_rhs;
public:
    // Constructor
    TermProd(TermPtr lhs, TermPtr rhs):m_lhs(lhs), m_rhs(rhs){ }
    // Copy constructor
    TermProd* clone() const
    {
        return new TermProd(m_lhs->clone(), m_rhs->clone());
    }
    // Destructor
    ~TermProd(){ delete m_lhs;delete m_rhs;}
    // Accessors
    Term* lhs() const { return m_lhs; }
    Term* rhs() const { return m_rhs; } 
    // Print
    void print() const
    {
        cout << "("; m_lhs->print(); cout << '*'; m_rhs->print(); cout << ")";
    }       
 };

Ответы [ 3 ]

6 голосов
/ 27 января 2012

Обратите внимание, что здесь вы не перезаписываете переменную x, а назначаете ее. Это вызовет значение по умолчанию operator= для вашего типа, что примерно приведет к выполнению следующего кода

  1. Выполнен конструктор TermProd::TermProd(TermPtr, TermPtr)
  2. Значения m_lhs и m_rhs копируются в значение x
  3. Деструктор для значения, созданного на шаге # 1, теперь запущен, а m_lhs и m_rhs удалены

На данный момент у вас есть реальная проблема. После шага № 2 значение x и временное значение, созданные на шаге № 1, имеют одинаковые значения m_lhs и m_rhs. Деструктор на шаге 3 удаляет их, но x все еще имеет ссылку на них, которая теперь эффективно указывает на мертвую память

Чтобы решить эту проблему, вам нужно добавить собственную operator=, которая правильно обрабатывает семантику назначения. Например

TermProd& operator=(const TermProd& other) {
  if (&other != this) {
    delete m_lhs;
    delete m_rhs;

    m_lhs = other.m_lhs->clone();
    m_rhs = other.m_rhs->clone();
  }
  return *this;
};

Чтобы быть корректным для всех сценариев, вам также нужно добавить правильный конструктор копирования

TermProd::TermProd(const TermProd& other) :
  m_lhs(other.m_lhs->clone()),
  m_rhs(other.m_rhs->clone())
{

}

Действительно, чтобы сделать это чрезвычайно просто, вы должны рассмотреть возможность использования std::shared_ptr<Term> в качестве значения для TermPtr. Это указатель, который сделает совместное использование без всех вышеупомянутых издержек

3 голосов
/ 27 января 2012

Вы не предоставляете конструктор копирования.У вас есть метод clone, который, как говорится в комментарии, является конструктором копирования, но это не так.

Попробуйте что-то вроде:

TermProd(TermProd const & other)
  m_lhs(other.m_lhs->clone()),
  m_rhs(other.m_rhs->clone())
{
}

И аналогично для других классов.

Обновление Как указано в комментариях, вам также понадобится оператор присваивания .

TermProd & operator=(TermProd const & other)
{
  if (this != &other)  // Check for assignment to self.
  {
    delete m_lhs;
    delete m_rhs;
    m_lhs = other.m_lhs->clone();
    m_rhs = other.m_rhs->clone();
  }
  return *this;
}
3 голосов
/ 27 января 2012

Вы не следуете правилу Три .
Уничтожение временных переменных, созданных во время вызовов функции, путем вызова неявного конструктора копирования delete s выделенного члена-указателя.

Вам необходимо предоставить свой собственный конструктор копирования и оператор присваивания копии, которые будут делать глубокие копии динамически размещенного элемента указателя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...