Почему при выводе значения я получаю значения мусора?
Я пытаюсь узнать об идиоме PIMPL и C++ в целом.
У меня есть класс с интерфейсом в стиле PIMPL, который устанавливает значение int равным 7. Но я получаю мусорное значение при печати, и я не понимаю, почему.
Код
test.cpp
#include <iostream>
#include "Test.h"
struct Foo::Bar
{
int value;
};
Foo::Foo()
{
Bar tempBar;
myBar = &tempBar;
myBar->value = 7;
}
void Foo::printValue()
{
std::cout << "Value = " << myBar->value << std::endl;
}
int main()
{
Foo myFoo;
myFoo.printValue();
return 0;
}
test.h
class Foo
{
private:
struct Bar;
Bar* myBar;
public:
Foo();
void printValue();
//~Foo();
};
Выход
Значение = 2147120498
4 ответа
mybar
указатель на локальную переменную внутри Foo
конструктор. Когда конструктор выходит, переменная исчезает, но myBar
все еще указывает на старую память.
Поскольку вы хотите реализовать PIMPL, на самом деле есть только один выбор. Вам нужно динамически распределять myBar
с помощью new
и освободить его delete
в Foo
деструктор. Вам также нужно будет добавить конструктор копирования и оператор присваивания копии Foo
также, чтобы избежать утечки памяти:
test.cpp
Foo::Foo()
{
myBar = new Bar;
myBar->value = 7;
}
Foo::Foo(const Foo &src)
{
myBar = new Bar;
*myBar = *(src.myBar);
}
Foo::~Foo()
{
delete myBar;
}
Foo& Foo::operator=(const Foo &rhs)
{
*myBar = *(rhs.myBar);
return *this;
}
test.h
class Foo
{
private:
struct Bar;
Bar* myBar;
public:
Foo();
Foo(const Foo &src);
~Foo();
void printValue();
Foo& operator=(const Foo &rhs);
};
Если бы вы не реализовывали PIMPL, тогда был бы другой вариант. Делать myBar
быть не указателем члена Foo
класс вместо:
test.cpp
Foo::Foo()
{
myBar.value = 7;
}
void Foo::printValue()
{
std::cout << "Value = " << myBar.value << std::endl;
}
test.h
class Foo
{
private:
struct Bar
{
int value;
};
Bar myBar;
public:
Foo();
void printValue();
};
Используя современный C++, вы можете сделать pimpl следующим образом:
#include <memory>
class Foo
{
struct Bar;
std::unique_ptr<Bar> myBar;
public:
Foo();
~Foo();
void printValue();
};
unique_ptr
является одним из немногих стандартных контейнеров библиотеки, который работает с неполными типами. Преимущество такого подхода заключается в том, что если вы допустите ошибку, например, закомментируете деструктор или попытаетесь скопировать Foo
тогда компилятор поймает это за вас.
Тела в файле.cpp могут быть:
Foo::Foo(): myBar( new Bar ) { myBar->value = 7; }
Foo::~Foo() {}
tempBar
выталкивается из стека в конце конструктора, так как это локальная переменная, поэтому вы используете неопределенное поведение.
Возможно, вы хотите использовать new
а также delete
в деструкторе.
Вы храните временный адрес. Вместо этого вам нужно выделить его:
Foo::Foo()
{
myBar = new Bar;
myBar->value = 7;
}
И тогда вы должны следовать правилу трех, предоставляя деструктор и конструктор копирования.