Мелкое копирование C++, глубокое копирование, а также стек, куча и статическая область хранения, понимание и опыт.

Прежде всего, стек, куча и статическая область хранения — это все открытые в памяти пространства. В частности, в C++, если вы создаете объект без использования new, то пространство для этого объекта открывается в стеке, например Нам не нужно беспокоиться о месте, программа автоматически освободит его после использования, и вам не нужно беспокоиться об утечках памяти.

Пространство, подобное этому типу пространства, является статическим пространством. В таком пространстве не нужно беспокоиться об утечках памяти. Статическое пространство — это пространство, которое открывается перед запуском программы.

Вот два способа использования статики:

Первый — статические переменные,

статический int a =10 ;

Фактически такую ​​переменную a можно использовать как глобальную переменную.

Второй тип — локальные статические переменные.

void a(int b){

статический int c = 10;

с+=1;

}

По сравнению с обычными локальными переменными, такие локальные статические переменные имеют более длительный жизненный цикл и не будут повторно инициализироваться при каждом вызове функции его значение будет обновлено в открывшемся пространстве.

Во-вторых, static также может изменять функции-члены, например

cpp
class Math {
public:
    static int add(int a, int b) {  // 静态函数
        return a + b;
    }
};

int main() { 
    int sum = Math::add(1, 2);  // 直接调用
}

Характеристики такой статической функции-члена очевидны с первого взгляда: ее можно вызывать без создания объекта.

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

Насколько я понимаю, для всех статически измененных функций или переменных они уже открыли пространство до запуска программы. Это пространство не зависит от каких-либо других факторов. Они не будут инициализироваться снова ни в какой ситуации и будут обновлены. и навсегда сохранил собственную ценность. На это не будут влиять другие факторы, и каждый может легко назвать это пространство или значение.Здесь есть несколько похожих глобальных переменных.

Затем давайте снова поговорим о куче. Куча похожа на стек. Как я только что сказал, стек — это пространство, открываемое без использования new при создании объекта, а куча — это пространство, открываемое с помощью нового ключевого слова, когда Создание объекта. Он создается с использованием нового символа в C++. Пространство открывается в куче. Этот вид пространства не управляется программой и контролируется программистом вручную. Поэтому пространство, открываемое новым, также является ситуация, когда легко возникают утечки памяти, поэтому этот способ освобождения места требует от нас ручного удаления. Есть два способа удаления:

cpp
Person::~Person() {
    delete this;   // 手动释放内存
}





cpp
Person* p = new Person();
// ...
delete p;  // 主动调用delete释放内存

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

На самом деле тут возникает вопрос: не генерирует ли C++ автоматически деструктор по умолчанию при создании класса, тогда зачем мне вручную писать деструктор для освобождения памяти? ?

Фактически принцип, упомянутый выше, остался прежним. В частности, мы используем ключевое слово new, чтобы освободить место в куче . Это пространство состоит из двух частей. Одна часть - это наш объект. Другая часть занимаемого пространства - это пространство, занятое некоторыми нестатическими переменными-членами.Системный деструктор по умолчанию автоматически освобождает эти нестатические переменные-члены, оставляя эту часть объекта. Занятое пространство необходимо освободить вручную, поэтому нам нужно вручную написать деструктор. (Подумайте об этом внимательно: если пространство кучи также может быть освобождено системным деструктором по умолчанию, то мы можем даже не увидеть деструктор.)

Далее давайте поговорим о поверхностном и глубоком копировании.

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

Конкретный код:

класс Фу {
    интервал м_р;
публика:
    Фу() {
        m_p = новый int (10);
    }
    
    Foo(const Foo& другое) {
        m_p = другое.m_p;  
<а я=0>
    
};
Красная часть — это конструктор копирования. При передаче переменных вы можете копировать и передавать значения в обычном режиме. Однако, если это указатель, вместо значения передается указатель.

код показан ниже:

cpp
class Foo {
    int* m_p;
public:
    Foo() {
        m_p = new int(10);
    }
    
    Foo(const Foo& other) {
        m_p = other.m_p;  // 拷贝指针,共享同一块内存
    }
};

int main() {
    Foo f1;
    Foo f2 = f1;  // 调用浅拷贝的拷贝构造函数
    
    delete f1.m_p;  // f1释放内存
    
    // f2.m_p指向已释放的内存,产生undefined behavior
}

После тщательного анализа мы видим, что созданный нами объект f1 автоматически вызывал конструктор при его создании, чтобы освободить 10 мест в куче для указателя m_p, а затем мы создали второй объект f2, так что f2=f1, то есть вызов нашего конструктора копирования, затем посмотрим, записано ли это в конструкторе копирования: m_p=other.m_p;Это предложение означает изменение m_p f1< a i=1>address< /span> передается в m_p из f2, верно? Это означает, что адрес переменной-члена m_p объекта f1 и адрес переменной-члена m_p объекта f2 теперь являются одним и тем же адресом. Это поверхностная копия, какие проблемы могут возникнуть? Первый известный нам способ - вручную освободить память кучи. Как видно из приведенного выше кода, мы используем delete, чтобы освободить память кучи f1, но переменная m_p f2 также указывает на этот адрес, и он освобождается. для него. К кому относится f2? Таким образом, это приведет к ситуации, называемой висячим указателем (называемой по-другому...). Эта программа сообщит об ошибке.

Во-вторых, мы забываем удалить открытое пространство кучи, что напрямую приведет к утечке памяти.

Так как же это решить?

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

Да, этот метод является глубоким копированием.

Конкретный код:

cpp
class Foo {
    int* m_p;
public:
    Foo() {
        m_p = new int(10);
    }
    
    Foo(const Foo& other) {
       // m_p = other.m_p;  // 拷贝指针,共享同一块内存
        m_p = new int(*other.m_p);
    }


};

~Foo(){

delete m_p;
}
int main() {
    Foo f1;
    Foo f2 = f1;  // 调用浅拷贝的拷贝构造函数
    
    delete f1.m_p;  // f1释放内存
    
    // f2.m_p指向已释放的内存,产生undefined behavior
}

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

Вот и все в двух словах. . Буду продолжать обновлять ~

Guess you like

Origin blog.csdn.net/qq_40962125/article/details/130769181