鱼C论坛

 找回密码
 立即注册
查看: 1270|回复: 10

[已解决]C++ 指针变量成员

[复制链接]
发表于 2023-5-16 15:28:38 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
指针变量成员,比如 char *tmp[10];在构造中需要通过 new() char[10]进行初始化吗?在析构中需要delete 这个指针数组吗?
最佳答案
2023-5-16 16:35:49
nl2020 发表于 2023-5-16 16:30
这种情况是在堆区申请空间好呢,还是直接实例化对象取地址赋值好呢?

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

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

这是一个例子:
Animal cat[10];  // 在栈上创建10个 Animal 对象
Animal* animals[10];

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

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

这是一个例子:
Animal* animals[10];

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

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

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

总的来说,选择在栈上创建对象还是在堆上动态分配内存,取决于你的特定需求和约束。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-5-16 15:29:09 | 显示全部楼层
在定义一个指针数组时,如 char *tmp[10],并不需要在构造函数中使用 new() char[10] 进行初始化。指针数组中的每个指针默认为野指针,即指向未知的内存地址。如果需要使用指针数组中的某个指针,则需要将其指向一个有效的内存地址。

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

需要注意的是,在使用指针数组时,应该始终检查指针是否为野指针或者指向未知的内存地址,以避免程序崩溃或出现其他问题。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-5-16 15:33:58 | 显示全部楼层
isdkz 发表于 2023-5-16 15:29
在定义一个指针数组时,如 char *tmp[10],并不需要在构造函数中使用 new() char[10] 进行初始化。指针数组 ...

那假如 把这个char 换成 Animal对象呢?什么时候需要在堆上开辟空间?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

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

在何时需要在堆上开辟空间,取决于你的应用需求。如果你的应用需要创建持久的 Animal 对象,并且在创建这些对象时,你可能无法确定对象的数量或生命周期,那么这是一个合适的时候在堆上为这些对象分配内存。例如:
for (int i = 0; i < 10; i++) {
    animals[i] = new Animal();
}

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

在析构函数中,如果你在堆上为 Animal 对象分配了内存,你就需要使用 delete 来释放这些内存。例如:
for (int i = 0; i < 10; i++) {
    delete animals[i];
}

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

注意:如果你不释放这些内存,当这个对象被销毁时,这些内存将不会被自动回收,这会导致内存泄漏。
[/b]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-5-16 15:53:11 | 显示全部楼层
本帖最后由 nl2020 于 2023-5-16 15:55 编辑
isdkz 发表于 2023-5-16 15:42
如果你定义了一个 Animal 对象的指针数组,如 Animal *animals[10],这个数组里存储的是指向 Animal 对象 ...


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

使用道具 举报

发表于 2023-5-16 15:56:57 | 显示全部楼层
本帖最后由 isdkz 于 2023-5-16 16:33 编辑
nl2020 发表于 2023-5-16 15:53
假如我已经实例化过 Animal的对象,比如 Animal Cat[10](对象已经存在了);我想把这些已经存在的对象 ...


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

你的代码应该是这样的:
// 创建 Cat 对象数组
Animal Cat[10];

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

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

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

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

如果你在给 animals 分配新空间后,再将 Cat 对象的地址赋值给 animals,那么你之前使用 new 分配的空间会丢失引用,无法被访问和释放,这就产生了内存泄漏。[/b]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-5-16 16:30:05 | 显示全部楼层
isdkz 发表于 2023-5-16 15:56
你的代码中有一些错误。如果你想将已经存在的对象的地址赋值给类的成员变量 Animal *animals[10],你不应 ...

这种情况是在堆区申请空间好呢,还是直接实例化对象取地址赋值好呢?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-16 16:35:49 | 显示全部楼层    本楼为最佳答案   
nl2020 发表于 2023-5-16 16:30
这种情况是在堆区申请空间好呢,还是直接实例化对象取地址赋值好呢?

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

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

这是一个例子:
Animal cat[10];  // 在栈上创建10个 Animal 对象
Animal* animals[10];

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

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

这是一个例子:
Animal* animals[10];

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

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

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

总的来说,选择在栈上创建对象还是在堆上动态分配内存,取决于你的特定需求和约束。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-16 17:12:34 | 显示全部楼层
nl2020 发表于 2023-5-16 16:30
这种情况是在堆区申请空间好呢,还是直接实例化对象取地址赋值好呢?

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

使用道具 举报

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

这么说最好还是使用C++的STL里面的容器吗?就不需要手动释放了吗
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-5-25 17:08:30 | 显示全部楼层
nl2020 发表于 2023-5-25 15:16
这么说最好还是使用C++的STL里面的容器吗?就不需要手动释放了吗

正确的实现 RAII 是核心
容器销毁时会自动销毁内部的元素,标准库中的模板类比如 std::unique_ptr 都会在被销毁时正确的释放其表示的资源(比如 std::unique_ptr 管理的指针)(换言之:都正确实现了 RAII,只要用法正确),因此一般无需手动处理,甚至无需过多关注
只要能用,为什么不用现成的工具和容器呢?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-10-7 15:25

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表