Это переназначение C ++ действительно? - PullRequest
2 голосов
/ 05 октября 2009

Извините за основной вопрос, но я не могу найти нужную вещь в Google.

#include <iostream>
#include <string>
using namespace std;

class C {
public: 
C(int n) {
    x = new int(n);
}

~C( ) {
    delete x;
}

int getX() {return *x;}

private: 
    int* x; 
}; 

void main( )  {
    C obj1 = C(3);
    obj1 = C(4);
    cout << obj1.getX() << endl;
}

Похоже, что он выполняет назначение правильно, затем вызывает деструктор в obj1, оставляя x со значением мусора, а не со значением 4. Если это действительно так, почему он это делает?

Ответы [ 7 ]

2 голосов
/ 05 октября 2009

Я не знаю достаточно глубокого, тонкого C ++, чтобы объяснить проблему, с которой вы сталкиваетесь. Однако я знаю, что гораздо легче убедиться, что класс ведет себя так, как вы ожидаете, если вы следуете правилу Трех , которое нарушает код, который вы опубликовали. По сути, в нем говорится, что если вы определяете любое из следующего, вы должны определить все три:

  • Destructor
  • Конструктор копирования
  • Оператор присваивания

Также обратите внимание, что реализация оператора присваивания должна правильно обрабатывать случай, когда объект присваивается самому себе (так называемое «самопредставление»). Следующее должно работать правильно (не проверено):

#include <iostream>
#include <string>
using namespace std;

class C {
public: 
C(int n) {
    x = new int(n);
}

C(const C &other): C(other.getX()) { }

~C( ) {
    delete x;
}

void operator=(const C &other) {
    // Just assign over x.  You could reallocate if you first test
    // that x != other.x (the pointers, not contents).  The test is
    // needed to make sure the code is self-assignment-safe.
    *x = *(other.x);
}

int getX() {return *x;}

private: 
    int* x; 
}; 

void main( )  {
    C obj1 = C(3);
    obj1 = C(4);
    cout << obj1.getX() << endl;
}
2 голосов
/ 05 октября 2009

Если C содержит указатель на что-то, вам почти всегда нужно реализовывать operator =. В вашем случае он будет иметь эту подпись

class C
{    
    public:
    void operator=(const C& rhs)
    {
        // For each member in rhs, copy it to ourselves
    } 
    // Your other member variables and methods go here...
};
2 голосов
/ 05 октября 2009

Если существует класс C, у которого есть конструктор, который принимает int, является ли этот код допустимым?

C obj1(3);
obj1=C(4);

Если C имеет operator=(C) (что будет по умолчанию), код действителен. Что произойдет, так это то, что в первой строке obj1 создается с 3 в качестве параметра для конструктора. Затем во второй строке создается временный объект C с параметром 4, а затем на obj1 вызывается operator= с этим временным объектом в качестве параметра. После этого временный объект будет разрушен.

Если obj1 находится в недопустимом состоянии после назначения (но не раньше), вероятно, существует проблема с C operator=.

Обновление: если x действительно должен быть указателем, у вас есть три варианта:

  1. Пусть пользователь вместо деструктора решит, когда следует удалить значение x, определив метод уничтожения, который пользователь должен явно вызывать. Это приведет к утечке памяти, если пользователь забудет это сделать.
  2. Определите operator=, чтобы он создавал копию целого числа вместо копии значения. Если в вашем реальном коде вы используете указатель на что-то намного большее, чем int, это может быть слишком дорого.
  3. Используйте подсчет ссылок, чтобы отслеживать, сколько экземпляров C содержит указатель на один и тот же объект, и удаляйте объект, когда его количество достигает 0.
1 голос
/ 05 октября 2009

В основном вы пытаетесь повторно реализовать умный указатель.
Это не тривиально, чтобы исправить все ситуации.

Пожалуйста, сначала посмотрите на доступные умные указатели в стандарте.

Базовая реализация (которая не срабатывает в определенных ситуациях (скопируйте одну из стандартных, чтобы получить лучшую)). Но это должно охватывать основы:

class X
{
    int*     data;

    public:
      // Destructor obvious
      ~X()
      {
          delete data;
      }
      // Easy constructor.
      X(int x)
          :data(new int(x))
      {}
      // Copy constructor.
      // Relatively obvious just do the same as the normal construcor.
      // Get the value from the rhs (copy). Note A class is a friend of
      // itself and thus you can access the private members of copy without
      // having to use any accessor functions like getX()
      X(X const& copy)
          :data(new int(copy.x))
      {}
      // Assignment operator
      // This is an example of the copy and swap idiom. This is probably overkill
      // for this trivial example but provided here to show how it is used.
      X& operator=(X const& copy)
      {
          X tmp(copy);
          this->swap(tmp);
          return this;
      }
      // Write a swap() operator.
      // Mark it is as no-throw.
      void swap(X& rhs) throws()
      {
          std::swap(data,rhs.data);
      }

  };
1 голос
/ 05 октября 2009

Будьте откровенны о владении указателями! auto_ptr отлично подходит для этого. Кроме того, при создании локального не делайте C obj1 = C(3), который создает два экземпляра C и инициализирует первый с конструктором копирования второго.

Внимай Гуру.

class C {
public:
   C(int n) : x(new int(n)) { }
   int getX(){ return *x; }
   C(const C& other) : x(new int(*other.x)){}
   C& operator=(const C& other) { *x = *other.x; return *this; }
private:
   std::auto_ptr<int> x;
};

int main() {
   C obj1(3);
   obj1 = C(4);
   std::cout << obj1.getX() << std::endl;
}
1 голос
/ 05 октября 2009

NEW: Происходит то, что ваш деструктор освободил память, выделенную конструктором C (4). Таким образом, указатель, который вы скопировали из C (4), является висящим указателем, т. Е. Он все еще указывает на место в памяти освобожденной памяти

class C {
public: 
C(int n) {
    x = new int(n);
}

~C( ) {
    //delete x; //Don't deallocate
}

void DeallocateX()
{
 delete x;
}

int getX() {return *x;}

private: 
    int* x; 
}; 

int main(int argc, char* argv[])
{
    // Init with C(3)
    C obj1 = C(3);
    // Deallocate C(3)
    obj1.DeallocateX();
    // Allocate memory and store 4 with C(4) and pass the pointer over to obj1
    obj1 = C(4);
    // Use the value
    cout << obj1.getX() << endl;
    // Cleanup
 obj1.DeallocateX();


 return 0;
}
0 голосов
/ 05 октября 2009

Когда вы проверяете значение obj1? Это после того, как вы покинете сферу?

В вашем примере obj1 является стековым объектом. Это означает, что как только вы покидаете функцию, в которой она определена, она очищается (вызывается деструктор). Попробуйте выделить объект в куче:

C *obj1 = new C(3);
delete obj1;
obj1 = new C(4);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...