Я запрограммировал небольшое программное обеспечение и хотел создать новый объект в куче. В функции члена класса я, таким образом,
void gShop::CreateCustomer(int type, int number)
{
vSlot[number] = new gCustopmer(type);
vSlot[number]->itsContactToShop=itsShopNumber;
vSlot[number]->itsNumber=number;
}
где vSlot - вектор указателей на объекты клиента. У меня есть (здесь: сокращенный) класс gShop, по существу:
class gShop : public gBranch
{
public:
gShop(): vSlot(100){}
~gShop(){}
std::vector <gCustomer*> vSlot;
...
}
и в основном я вызываю функцию-член для создания новых клиентов.
vShop[0].CreateCustomer(TYPE_M20, 1);
vShop[0].CreateCustomer(TYPE_F40, **2**);//EDIT:typo previously here. I intend to create customers by reading a file later on.
std::cout<< "1" << vShop[0].vSlot[1]->itsTypeString << std::endl;
std::cout<< "2" << vShop[0].vSlot[2]->itsTypeString << std::endl;
Я знаю, что я создал с "новыми" двумя объектами в "куче" (если я правильно отношусь к терминологии - извините, я совершенно новичок в программировании без формального образования), и у меня также есть два указателя на объекты, хранящиеся в векторе внутри магазин объектов [0].
Мой вопрос: я слышал высказывание, что для каждого нового есть удаление. Где я должен удалить этот объект? На самом деле я не планирую удалять какой-либо созданный магазин или объект клиента в программе.
Во-вторых, этот код до сих пор хорошо подходит для того, чтобы не вызывать утечки памяти? Я немного обеспокоен тем, что создал новый объект в классе функций-членов, поэтому я должен попытаться реализовать delete в деструкторе в gShop и установить указатель на NULL - в теоретическом случае я должен когда-либо захотеть удалить магазин [0]?
Большое спасибо.
Каждый раз, когда вы создаете объект с new
куском памяти, берется из кучи. Единственный способ связаться с этой памятью - это указатель, который вы получили от new
. Когда вы используете delete
эта память указателя освобождается и может использоваться для других целей. Поэтому, когда вы теряете указатель, вы создаете утечку памяти.
В вашем коде правильный путь:
Начните с вектора с нулевыми указателями. Когда вы создаете объект в точном указателе проверки позиции. Если он не является нулевым, у вас уже есть объект и его нужно удалить (или, возможно, выполнить ошибку)
void gShop::CreateCustomer(int type, int number)
{
if(vSlot[number] != 0) {
delete vSlot[number];
vSlot[number] = 0;
}
vSlot[number] = new gCustopmer(type);
vSlot[number]->itsContactToShop=itsShopNumber;
vSlot[number]->itsNumber=number;
}
Когда вектор уничтожается, вам нужно освободить всю память внутри него. Таким образом, у вас деструктор будет что-то вроде этого:
gShop::~gShop() {
for(int i = 0; i < (int)vSlot.size(); ++i) {
delete vSlot[i];
}
}
В том, как вы написали свой код, вы должны расширить реализацию деструктора для gShop
чтобы gShop
vector<> vSlot
и delete
каждый элемент. Поскольку у вас есть память, которая должна управляться таким образом, чтобы предотвратить утечку памяти, вам также необходимо следовать правилу 3. Таким образом, вам также нужно что-то сделать во время создания копии (глубокая копия), и вам нужно что-то сделать для оператора присваивания (очистить vector<>
который скоро будет скопирован, и сделать глубокую копию).
Вы можете избежать этих проблем и позволить своему объекту использовать деструктор по умолчанию, конструктор копирования и оператор присваивания, используя вместо этого интеллектуальный указатель. Например:
std::vector<std::shared_ptr<gCustomer>> vSlot;
Когда вы создаете элемент в vSlot
, вы можете использовать make_shared()
:
vSlot[number] = std::make_shared<gCustopmer>(type);
Умный указатель удалит память для вас, когда больше нет ссылок на память. Если у вас нет доступных C++.11, вместо этого вы можете использовать boost::shared_ptr
.
Умный указатель сделает это так, чтобы копия вашего gShop
разделила те же указатели, что и исходный gShop
он скопировал. Умный указатель делает эту ситуацию в порядке, поскольку в одной и той же памяти не будет нескольких вызовов delete
. Однако, если вам нужна семантика глубокой копии, вам все равно придется реализовать собственный конструктор копий и оператор присваивания, чтобы сделать глубокие копии.
Если вы хотите что-то, что будет автоматически очищаться, как смарт-указатель, но все равно дадите вам глубокую копию с конструктором копии по умолчанию и оператором присваивания по умолчанию, вы можете попробовать использовать boost::optional
.
Если вы используете g++
версии 4.4 или выше, то вы должны иметь возможность включить функции C++.11 с помощью -std=gnu++0x
или -std=C++0x
если вы не хотите Расширения GNU. Если у вас есть g++
4,7 или выше, то это опции -std=gnu++11
или -std=C++11
.
>>
до > >
. Кроме того, хорошая работа по упоминанию глубокого копирования.
std::shared_ptr
- это C ++ 11, поэтому закрытие нескольких шаблонов подряд без пробелов >>
вполне нормально.
gShop
необходимо delete
объекты gCustomer
которые он создает. Он может сделать это в своем деструкторе, например:
class gShop : public gBranch
{
public:
...
~gShop()
...
};
gShop::~gShop()
{
std::vector<gCustomer*>::iterator iter = vSlot.begin();
std::vector<gCustomer*>::iterator end = vSlot.end();
while (iter != end)
{
delete *iter;
++iter
}
}
Или:
void deleteCustomer(gCustomer *customer)
{
delete customer;
}
gShop::~gShop()
{
std::for_each(vSlot.begin(), vSlot.end(), deleteCustomer);
}
Однако вы все равно будете иметь утечку памяти. Вы сохраняете два отдельных объекта gCustomer
в одном и том же vSlot[1]
vShop[0]
, поэтому вы теряете vShop[0]
над одним из ваших клиентов. Я подозреваю, что вы хотели сделать это вместо этого:
vShop[0].CreateCustomer(TYPE_M20, 1);
vShop[0].CreateCustomer(TYPE_F40, 2); // <-- was previously 1
std::cout<< "1" << vShop[0].vSlot[1]->itsTypeString << std::endl;
std::cout<< "2" << vShop[0].vSlot[2]->itsTypeString << std::endl;
При этом вам следует подумать о своем дизайне и позволить STL обрабатывать все управление памятью для вас, например:
class gShop : public gBranch
{
public:
std::vector <gCustomer> vSlot;
...
};
void gShop::CreateCustomer(int type)
{
vSlot.push_back(type);
gCustomer &cust = vSlot.back();
cust.itsContactToShop = itsShopNumber;
cust.itsNumber = vSlot.size()-1;
}
vShop[0].CreateCustomer(TYPE_M20);
vShop[0].CreateCustomer(TYPE_F40);
// remember that vectors are 0-indexed
std::cout<< "0" << vShop[0].vSlot[0].itsTypeString << std::endl;
std::cout<< "1" << vShop[0].vSlot[1].itsTypeString << std::endl;
Или:
class gShop : public gBranch
{
public:
std::vector <std::shared_ptr<gCustomer> > vSlot;
...
};
void gShop::CreateCustomer(int type)
{
std::shared_ptr customer = std::make_shared<gCustomer>(type);
customer->itsContactToShop = itsShopNumber;
customer->itsNumber = vSlot.size();
vSlot.push_back(customer);
}
vShop[0].CreateCustomer(TYPE_M20);
vShop[0].CreateCustomer(TYPE_F40);
std::cout<< "0" << vShop[0].vSlot[0]->itsTypeString << std::endl;
std::cout<< "1" << vShop[0].vSlot[1]->itsTypeString << std::endl;
shared_ptr
является новым в C ++ 11. Для более старых версий C ++ вместо этого вы можете использовать boost::shared_ptr
.
Да, любая память, выделенная из кучи, должна быть освобождена! С "новым" вы выделяете память из кучи, которая является sizeof gCustomer. Хорошее место для освобождения вашей памяти будет в вашем деструкторе: ~ gShop() Утечки памяти вызваны не освобождением памяти, хотя после того, как вы закроете свою программу, вся память будет автоматически освобождена.