Это законный подход для создания локальной переменной в C++

Я новичок в 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 создается в стеке, но инициализируется значением кучи (из-за нового). Если используется новый оператор, где должно применяться удаление?

Спасибо за любые предложения!

MyClass myLocalObject = new MyClass(); точно не скомпилируется.
V.K. author of HiFile 06.05.2022 17:00

@V.K.authorofHiFile Будет. У него есть конвертирующий конструктор. :)

David G 06.05.2022 17:00

Вы определенно пропускаете объект. 'new MyClass()' передается в ""копировщик"" (на самом деле это не копировщик, но выполняет аналогичную работу) и никогда не удаляется.

Borgleader 06.05.2022 17:01

@DavidG О, ты прав, я пропустил этот конструктор. ... но какой это уродливый код! :)

V.K. author of HiFile 06.05.2022 17:02

Точно так же векторы не вызывают удаление указателей, которые они содержат при уничтожении, либо избегайте использования новых объектов, либо сохраняйте их внутри std::unique_ptr (и аналогично std::vector<std::unique_ptr<T>>)

Borgleader 06.05.2022 17:02
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

У вас есть утечка памяти в 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 содержит большие данные - десятки мегабайт трехмерных кривых (линии и дуги, точки и т. д.). Просто постарайтесь найти правильный подход.

bioan 06.05.2022 17:29

В основном есть 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 для каждого элемента.

Надеюсь, это поможет :-)

Большое тебе спасибо!! Конечно, я должен изучить умные указатели!!!

bioan 06.05.2022 17:23

Конечно :-) Возможно, ответы на этот вопрос помогут вам освоиться: stackoverflow.com/questions/106508/… Динамическое управление памятью, безусловно, полезно, и имхо работа с необработанными указателями хороша для обучения, но, вероятно, в самом начале разочаровывает.

Chris G. 06.05.2022 17:26

:-) У меня тоже эта страница открыта на компе для изучения. Благодарю вас!

bioan 06.05.2022 17:32

Другие вопросы по теме