Передача по значению в C ++ - PullRequest
0 голосов
/ 07 августа 2011

Здесь объект передается по значению, я не понимаю вывод программы.

#include <iostream>
using namespace std;

class Sample
{
public:
    int *ptr;

    Sample(int i)
    {
        ptr = new int(i);
    }
    ~Sample()
    {
        delete ptr;
    }
    void PrintVal()
    {
        cout << "The value is " << *ptr;
    }
};

void SomeFunc(Sample x)
{
    cout << "Say i am in someFunc " << endl;
}

int main()
{
    Sample s1= 10;
    SomeFunc(s1);
    s1.PrintVal();
}

Ответы [ 4 ]

8 голосов
/ 07 августа 2011

Вы забыли написать конструктор копирования .Компилятор генерирует один для вас, который просто копирует значение указателя, не делая глубокую копию.Таким образом, когда передаваемый параметр копируется и уничтожается, ваш исходный объект пытается вызвать delete второй раз для того же указателя, что является неопределенным поведением.

Запомните Правило трех .
Вкратце:
Если вам необходимо определить любой из copy-constructor, copy-assignment operatorили destructor, тогда вы, вероятно, захотите определить и другие два.В этом случае ваш copy constructor должен выглядеть примерно так:

Sample(const Sample& other)
      :ptr(new int(*other.ptr));
{
}
2 голосов
/ 07 августа 2011

Вывод здесь должен представлять собой сбой, который я себе представляю.

Проблема здесь в том, что когда вы передаете по значению, ваш объект Sample копируется.В вашем скопированном объекте вы не выделяете новый ptr.Поэтому, когда копия s1 будет уничтожена, она удалит исходный s1.

Добавьте ниже конструктор копирования, чтобы получить желаемое поведение.

Sample(const Sample &s)
{
    ptr = new int(*(s.ptr));
}
1 голос
/ 07 августа 2011

Проблема

Конструктор копирования по умолчанию в вашем классе, предоставляемый компилятором, просто копирует указатель вместо целого числа, на которое указывает указатель.Итак, теперь у вас есть два указателя, указывающие на один объект.Когда один из объектов выходит из области видимости, указатель внутри другого объекта становится dangling pointer.Попытка доступа к висячему указателю почти всегда будет беспокоить вас во время выполнения, например, сбой вашей программы.

int main()
{
    Sample s1= 10;
    // copying by value here creates a local Sample object inside SomeFunc
    // which points to the same heap-allocated integer. After it goes out
    // of its scope(leaves this function), that memory is released. Then 
    // "ptr" in s1 becomes a dangling pointer.
    SomeFunc(s1);
    s1.PrintVal();
}

Решение

Если у вас есть какой-либо указатель на член классауказывает на выделенный в куче буфер, вам необходимо явно перегрузить конструктор копирования и оператор присваивания = для глубокого копирования этих элементов указателя, т. е. выделить новую память и скопировать данные, на которые указывают эти указатели.

0 голосов
/ 07 августа 2011

Проблема в том, что ваш класс содержит указатель.

Реальный код очень редко должен содержать OWNED указатели.Они обычно заключены в некоторый служебный класс (например, интеллектуальный указатель или контейнер) или, чаще всего, это простые объекты.

Ваш код должен выглядеть следующим образом:

class Sample
{
public:
    int val;    // No need to make it a pointer

    Sample(int i)
       : val(i)
    {}
    void PrintVal()
    {  cout << "The value is " << val;
    }
};

Если вам нужнодинамически размещенный объект, тогда он должен быть в интеллектуальном указателе (если вы хотите использовать общий ресурс, тогда он выглядит следующим образом.

class Sample
{
public:
    std::shared_ptr<int>  ptr;

    Sample(int i)
        :ptr(new int(i))
    {}
    void PrintVal()
    {
        cout << "The value is " << *ptr;
    }
};

Предполагается, что вы пишете свою собственную оболочку указателя (плохая идея), но давайте предположим,это для практики с указателями. Тогда вам нужно подчиниться правилу 3. Это потому, что сгенерированные компилятором версии copy constructor и assignment operator не будут работать правильно для OWNED RAW указателей. OWNED RAW Указатель - это указатель, которым вы владеете и, таким образом, отвечаете за удаление.

Существует два решения этой проблемы. Сделайте конструктор копирования и операторы назначения частными. Это остановит вашкод работает, поэтому я предполагаю, что это не то, что вам нужно. Поэтому нам нужно заставить их работать. Так как общий случай описан выше в std:: shared_ptr <> здесь мы рассмотрим случай совместного использования копий.

class Sample
{
public:
    int *ptr;   // OWNED RAW POINTER

    Sample(int i)
       : ptr(new int(i))
    {}
    ~Sample()
    {
        delete ptr;
    }
    Simple(Simple const& copy)
        : ptr(new int (*(copy.ptr)))
    {}
    Simple& operator=(Simple rhs)    // Assignment uses the copy and swap idium.
    {                                // Notice the parameter is passed by value to get
        std::swap(ptr, rhs.ptr);     // an implicit copy using the copy constructor.
    }                                // Then we simply swap the pointers. The copy will be
                                     // destroyed with the old pointer.

    void PrintVal()
    {
        cout << "The value is " << *ptr;
    }
};
...