nl2020 发表于 2023-5-16 15:28:38

C++ 指针变量成员

指针变量成员,比如 char *tmp;在构造中需要通过 new() char进行初始化吗?在析构中需要delete 这个指针数组吗?

isdkz 发表于 2023-5-16 15:29:09

在定义一个指针数组时,如 char *tmp,并不需要在构造函数中使用 new() char 进行初始化。指针数组中的每个指针默认为野指针,即指向未知的内存地址。如果需要使用指针数组中的某个指针,则需要将其指向一个有效的内存地址。

在析构函数中,如果指针数组中的每个指针都指向了通过 new() 分配的内存空间,则需要使用 delete[] 将其释放。否则,如果某个指针指向的是栈上的变量或全局变量等,不应该使用 delete[] 进行释放,否则会导致程序崩溃。

需要注意的是,在使用指针数组时,应该始终检查指针是否为野指针或者指向未知的内存地址,以避免程序崩溃或出现其他问题。

nl2020 发表于 2023-5-16 15:33:58

isdkz 发表于 2023-5-16 15:29
在定义一个指针数组时,如 char *tmp,并不需要在构造函数中使用 new() char 进行初始化。指针数组 ...

那假如 把这个char 换成 Animal对象呢?什么时候需要在堆上开辟空间?

isdkz 发表于 2023-5-16 15:42:40

nl2020 发表于 2023-5-16 15:33
那假如 把这个char 换成 Animal对象呢?什么时候需要在堆上开辟空间?

如果你定义了一个 Animal 对象的指针数组,如 Animal *animals,这个数组里存储的是指向 Animal 对象的指针,而不是 Animal 对象本身。这些指针默认都是野指针。

在何时需要在堆上开辟空间,取决于你的应用需求。如果你的应用需要创建持久的 Animal 对象,并且在创建这些对象时,你可能无法确定对象的数量或生命周期,那么这是一个合适的时候在堆上为这些对象分配内存。例如:

for (int i = 0; i < 10; i++) {
    animals = new Animal();
}


在这个例子中,我们为指针数组中的每个指针都分配了一个新的 Animal 对象。

在析构函数中,如果你在堆上为 Animal 对象分配了内存,你就需要使用 delete 来释放这些内存。例如:

for (int i = 0; i < 10; i++) {
    delete animals;
}


在这个例子中,我们释放了指针数组中每个指针所指向的 Animal 对象的内存。

注意:如果你不释放这些内存,当这个对象被销毁时,这些内存将不会被自动回收,这会导致内存泄漏。

nl2020 发表于 2023-5-16 15:53:11

本帖最后由 nl2020 于 2023-5-16 15:55 编辑

isdkz 发表于 2023-5-16 15:42
如果你定义了一个 Animal 对象的指针数组,如 Animal *animals,这个数组里存储的是指向 Animal 对象 ...

假如我已经实例化过 Animal的对象,比如 Animal Cat(对象已经存在了);我想把这些已经存在的对象的地址赋值给类的成员变量Animal *animals,而且我也通过 new() 方法在堆区申请了空间,我能进行如下赋值操作吗?
for (int i = 0; i < 10; i++) {
    animals = new Animal();
}
for (int i = 0; i < 10; i++) {
    animals = &Cat ;
}
会产生什么样的影响呢?这个时候析构还需要释放堆区内存吗?(好像找不到了吧)

isdkz 发表于 2023-5-16 15:56:57

本帖最后由 isdkz 于 2023-5-16 16:33 编辑

nl2020 发表于 2023-5-16 15:53
假如我已经实例化过 Animal的对象,比如 Animal Cat(对象已经存在了);我想把这些已经存在的对象 ...

你的代码中有一些错误。如果你想将已经存在的对象的地址赋值给类的成员变量 Animal *animals,你不应该在此之前给 animals 的元素分配新的空间。如果你这样做,会导致内存泄漏,因为你将丢失对这些新分配的 Animal 对象的引用。

你的代码应该是这样的:

// 创建 Cat 对象数组
Animal Cat;

// 你的类的成员变量
Animal *animals;

// 将 Cat 对象的地址赋值给 animals
for (int i = 0; i < 10; i++) {
    animals = &Cat;
}


在这段代码中,我们将每个 Cat 对象的地址赋值给了 animals 数组中对应的元素。这样,animals] 指针就指向了 Cat] 对象。

注意,这里我们没有使用 new 创建新的 Animal 对象,而是使用了已经存在的对象的地址。因此,我们也不需要在析构函数中使用 delete 来释放这些地址,因为这些 Cat 对象会在它们的生命周期结束时自动被销毁。

如果你在给 animals 分配新空间后,再将 Cat 对象的地址赋值给 animals,那么你之前使用 new 分配的空间会丢失引用,无法被访问和释放,这就产生了内存泄漏。

nl2020 发表于 2023-5-16 16:30:05

isdkz 发表于 2023-5-16 15:56
你的代码中有一些错误。如果你想将已经存在的对象的地址赋值给类的成员变量 Animal *animals,你不应 ...

这种情况是在堆区申请空间好呢,还是直接实例化对象取地址赋值好呢?

isdkz 发表于 2023-5-16 16:35:49

nl2020 发表于 2023-5-16 16:30
这种情况是在堆区申请空间好呢,还是直接实例化对象取地址赋值好呢?

这取决于你的具体需求和使用情况。

如果你的对象的生命周期是固定的,并且在编译时已知(例如,你知道你只需要10个 Animal 对象,并且它们在函数或作用域结束时可以被销毁),那么在栈上创建对象并将地址赋给指针数组可能是更好的选择。这样做的好处是,你不需要手动管理内存(如使用 new 和 delete)。

这是一个例子:

Animal cat;// 在栈上创建10个 Animal 对象
Animal* animals;

for (int i = 0; i < 10; i++) {
    animals = &cat;// 将地址赋给指针数组
}


但是,如果你的对象的数量或生命周期在运行时可能会变化(例如,你可能在运行时决定需要更多的 Animal 对象,或者你可能需要在多个函数或作用域之间共享这些对象),那么在堆上动态分配内存可能是更好的选择。这样做的好处是,你可以更灵活地管理你的对象的生命周期和数量,但是缺点是你需要手动管理内存,确保在适当的时候使用 delete 来释放内存,防止内存泄漏。

这是一个例子:

Animal* animals;

for (int i = 0; i < 10; i++) {
    animals = new Animal();// 在堆上创建 Animal 对象并将地址赋给指针数组
}

// 使用这些 Animal 对象...

for (int i = 0; i < 10; i++) {
    delete animals;// 当你不再需要这些 Animal 对象时,记得释放内存
}


总的来说,选择在栈上创建对象还是在堆上动态分配内存,取决于你的特定需求和约束。

dolly_yos2 发表于 2023-5-16 17:12:34

nl2020 发表于 2023-5-16 16:30
这种情况是在堆区申请空间好呢,还是直接实例化对象取地址赋值好呢?

既然使用的是 C++,那么 RAII 就是用来解决这一问题的强而有力的手段。正确的使用 RAII 基本可以免除绝大多数内存相关的错误而无需额外的心智负担,可能问题就剩下生命周期了(Rust:我的强项)。
比如如果可能的话,直接用 std::vector 之类的容器保存对象的实例而不是指针,这也能支持数量动态变化;如果需要跨函数存活等比较复杂的情况,简单考虑可以直接用 std::unique_ptr 这样的智能指针,它们会自动处理析构和内存释放,也会禁止复制来避免多重释放、释放后访问等问题,要记得正确实现自定义类的构造析构函数
总之,既然使用 C++,就使用或者至少尝试一下现代一点的内存管理方式吧,new/delete 走天下虽然能走,但对自己和别人的精神不好

nl2020 发表于 2023-5-25 15:16:10

dolly_yos2 发表于 2023-5-16 17:12
既然使用的是 C++,那么 RAII 就是用来解决这一问题的强而有力的手段。正确的使用 RAII 基本可以免除绝大 ...

这么说最好还是使用C++的STL里面的容器吗?就不需要手动释放了吗

dolly_yos2 发表于 2023-5-25 17:08:30

nl2020 发表于 2023-5-25 15:16
这么说最好还是使用C++的STL里面的容器吗?就不需要手动释放了吗

正确的实现 RAII 是核心
容器销毁时会自动销毁内部的元素,标准库中的模板类比如 std::unique_ptr 都会在被销毁时正确的释放其表示的资源(比如 std::unique_ptr 管理的指针)(换言之:都正确实现了 RAII,只要用法正确),因此一般无需手动处理,甚至无需过多关注
只要能用,为什么不用现成的工具和容器呢?
页: [1]
查看完整版本: C++ 指针变量成员