Я новичок в C++ и пытаюсь понять, как создать и использовать класс в C++. Для этого у меня есть следующий код:
class MyClass
{
public:
MyClass()
{
_num = 0;
_name = "";
}
MyClass(MyClass* pMyClass)
{
_num = pMyClass->_num;
_name = pMyClass->_name;
}
void PrintValues() { std::cout << _name << ":" << _num << std::endl; }
void SetValues(int number, std::string name)
{
_num = number;
_name = name;
}
private:
int _num;
std::string _name;
};
int main()
{
std::vector<MyClass*> myClassArray;
MyClass myLocalObject = new MyClass();
for (int i = 0; i < 3; i++)
{
myLocalObject.SetValues(i, "test");
myClassArray.push_back(new MyClass(myLocalObject));
}
myClassArray[1]->PrintValues();
// use myClassArray further
}
Я получаю аналогичный пример из Интернета и пытаюсь его понять. Мои намерения состоят в том, чтобы заполнить myClassArray новыми объектами класса. Если я скомпилирую приведенный выше код с помощью VisualStudio 2022, я не получу ошибок, но я не уверен, что это не приведет к утечке памяти или есть ли более быстрый и простой подход.
Особенно мне непонятна следующая строчка: MyClass myLocalObject = новый MyClass();
myLocalObject создается в стеке, но инициализируется значением кучи (из-за нового). Если используется новый оператор, где должно применяться удаление?
Спасибо за любые предложения!
@V.K.authorofHiFile Будет. У него есть конвертирующий конструктор. :)
Вы определенно пропускаете объект. 'new MyClass()' передается в ""копировщик"" (на самом деле это не копировщик, но выполняет аналогичную работу) и никогда не удаляется.
@DavidG О, ты прав, я пропустил этот конструктор. ... но какой это уродливый код! :)
Точно так же векторы не вызывают удаление указателей, которые они содержат при уничтожении, либо избегайте использования новых объектов, либо сохраняйте их внутри std::unique_ptr (и аналогично std::vector<std::unique_ptr<T>>)
У вас есть утечка памяти в MyClass myLocalObject = new MyClass();
, поскольку динамически выделяемый объект используется для преобразования-создания нового myLocalObject
(это был почти, но не совсем конструктор копирования), а затем указатель теряется.
Вы также не показали код с использованием вектора, но если он не удаляет указатели внутри, у вас будет больше утечек памяти.
Нет причин иметь почти копирующий конструктор; компилятор предоставил вам лучший настоящий конструктор копирования.
Более быстрый и простой подход состоит в том, чтобы признать, что этот код вообще не нуждается в указателях.
class MyClass
{
public:
MyClass()
: _num(), _name() // initialize is better than assignment
{
//_num = 0;
//_name = "";
}
// compiler provides a copy constructor taking const MyClass&
//MyClass(MyClass* pMyClass)
//{
// _num = pMyClass->_num;
// _name = pMyClass->_name;
//}
void PrintValues() { std::cout << _name << ":" << _num << std::endl; }
void SetValues(int number, std::string name)
{
_num = number;
_name = name;
}
private:
int _num;
std::string _name;
};
int main()
{
std::vector<MyClass> myClassArray; // not a pointer
MyClass myLocalObject; // = new MyClass(); // why copy a default instance when you can just default initialize?
for (int i = 0; i < 3; i++)
{
myLocalObject.SetValues(i, "test"); // works just as before
myClassArray.push_back(/*new MyClass*/(myLocalObject)); // don't need a pointer, vector knows how to copy objects
// also, this was using the "real" copy-constructor, not the conversion from pointer
}
myClassArray[1].PrintValues(); // instead of ->
// use myClassArray further
}
для случаев, когда необходим указатель, например полиморфизм, используйте интеллектуальный указатель:
std::vector<std::unique_ptr<MyClass>> myClassArray; // smart pointer
myClassArray.push_back(make_unique<MyDerivedClass>(stuff));
std::unique_ptr
автоматически освободит объект, когда он будет удален из вектора (если вы не переместите его явно), избегая необходимости запоминать delete
.
На самом деле myClassArray содержит большие данные - десятки мегабайт трехмерных кривых (линии и дуги, точки и т. д.). Просто постарайтесь найти правильный подход.
В основном есть 2 способа создания экземпляров объектов классов.
Динамическое размещение (в куче)
MyClass* myLocalObject = new MyClass(); // dynamically allocates memory and assigns memory address to myLocalObject
Пример для вашего цикла:
class MyClass
{
private:
int _num;
std::string _name;
public:
// let's add an additional constuctor having default values
// that makes it easier later on
// if parameters are passed, they are used, or the defalt values, if not
// can call it like MyClass(), MyClass(123), or MyClass(456,"hello")
// you might want to pass larger data as reference, to avoid copying it
MyClass(int num=0, std::string name = "some default text")
: _num(num), _name(name)
{}
};
std::vector<MyClass*> myClassArray; // your array of pointers
for (int i = 0; i < 3; i++)
myClassArray.push_back(new MyClass(i, "test"));
// delete
for (auto& pointerToElement : myClassArray) // get a reference to each element (which is a pointer)
delete pointerToElement; // delete element (call's destructor if defined)
В этом случае вы должны delete myLocalObject;
или вы получите утечку памяти.
Вместо того, чтобы иметь дело с необработанными указателями, особенно новичкам в C++, я рекомендую использовать интеллектуальные указатели, которые занимаются управлением памятью вместо вас.
Автоматическое распределение (по возможности в стеке)
MyClass myLocalObject = MyClass(); // automatically allocates memory and creates myLocalObject
Обычно это происходит в стеке (если возможно). Это намного быстрее, и вам не нужно иметь дело с динамическим управлением памятью.
Пример для вашего цикла:
std::vector<MyClass> myClassArray; // now "containg" the memory of objects itself
for (int i = 0; i < 3; i++)
{
myClassArray.emplace_back(i, "test"); // we use emplace_back instead to construct instances of type MyClass directly into the array
}
// no deletion required here
// destructors of each element will be called (if defined) when myClassArray is deleted automatically when out of scope
Есть и другие способы, вроде динамического распределения стека и прочей черной магии, но рекомендуем ориентироваться на «стандарт».
В случае работы с большими объемами данных вы можете использовать std::vector::reserve. В сочетании с автоматическим распределением/распределением стека это помогает значительно ускориться, ограничивая выделение памяти до 1 вообще вместо 1 для каждого элемента.
Надеюсь, это поможет :-)
Большое тебе спасибо!! Конечно, я должен изучить умные указатели!!!
Конечно :-) Возможно, ответы на этот вопрос помогут вам освоиться: stackoverflow.com/questions/106508/… Динамическое управление памятью, безусловно, полезно, и имхо работа с необработанными указателями хороша для обучения, но, вероятно, в самом начале разочаровывает.
:-) У меня тоже эта страница открыта на компе для изучения. Благодарю вас!
MyClass myLocalObject = new MyClass();
точно не скомпилируется.