目录
前言
01 在堆栈上创建对象
02 堆栈上创建对象有什么区别
03 在栈上实例化对象
04 在堆中实例化对象
前言
本章我们讨论一下 C++ 创建对象的相关问题。
如果你还不了解什么是类,可以点击下文查看
P9 C++类-CSDN博客
本章以下主要讲解以下几点
- 在栈上创建对象
- 在堆上创建对象
- 堆栈上创建对象有何区别
- delete删除对象,避免内存泄漏。
01 在堆栈上创建对象
C++ 给了我们一些创建对象的方法。当我们写完一个类,然后开始使用我们创建的类,除非它是完全静态的,不然我们需要实例化这个类。
创建类对象的方式有两种,这两种方法之间的区别是内存主要是 从哪里来的,在哪里创建对象。
当我们创建一个 C++ 对象时,它需要占用一些内存,即便我们写一个完全为空的类,类中没有成员,它至少也要占用一个字节的内存。
通常情况下,类中会有很多成员,它们存储在某些地方,当我们使用这个对象的时候,会创建一堆变量,会需要在电脑的某个地方分配内存,这样就可以记住这些变量设置的值。
程序会将内存主要分为两部分,栈和堆,当然还有其它部分的内存,如源代码的区域,这些都是机器代码
02 堆栈上创建对象有什么区别
在 C++ 中,我们选择对象要放在哪里,是在栈上还是堆上创建,有不同的功能差异,
- 栈对象,它有一个自动的生存周期,它们的生存期实际是由它声明的地方作用域决定的,只要变量超出作用域,相应的内存就被释放了,因为当作用域结束的时候,栈会弹出作用域里面的东西,栈上的任何东西都会被释放。
- 但堆是不同的,一旦在堆中分配一个对象,它会一直呆在那里,直到你做出决定,——我不再需要它了,我想释放这个对象,你想怎么处置那段内存都行。
我们来看看这两种创建对象的方法的代码是什么样子的。
class Animal
{
private:std::string m_name; int m_age;double m_weight;
public:void setName(std::string pname) {m_name = pname;}void getName(){std::cout << m_name << std::endl;}
};
上面的代码案例是一个叫做 Animal的类,它只有一个字符串成员m_m_name,一个整形m_age,双精度m_weight以及一个简单的 setName 方法和getName方法。
接下来在主函数中分别用两种方式创建这个类的实例。
03 在栈上实例化对象
第一种方法是在栈上创建,它调用了默认构造函数,可以通过调用它的 setName设置对象的名字,getName 方法打印它的 m_Name,运行代码可以得到 "Dog"。
我们可以在几乎所有的场景下使用上面的创建方式,如果能这样创建就尽量这样创建,这就是最基本的规则。
04 在堆中实例化对象
那什么时候在堆中实例化对象呢?
- 如果你想把 Animal放在这个函数生存期之外,比如有一个函数,你想在函数开始的位置创建对象,在函数结束的位置把它在内存中销毁,那就在栈上创建对象即可,但如果你想让它的生命周期延长,你就应该在堆中实例化对象了
- 但如果这个 Animal的规模太大,而且我们可能有太多的 animal,我们可能没有足够的空间在栈上分配,因为栈通常非常小,大概1、2M。
以上的两种情况下,就可能必须在堆上进行分配,下面是具体的操作。
首先先改变它的类型,然后使用 new 关键字,new Entity 会返回这个 entity 在堆上被分配的内存地址。
简单来说,在堆上分配要比栈花费更长的时间,而且在堆上分配内存的话,必须手动释放被分配的内存,所以一旦在堆上分配了内存空间,我们需要使用 delete
来释放这些内存。
所以不是所有的情况都要采取在堆上分配空间。
这就是我们创造对象的两种方法,那么如何选择呢?你可以参考这些标准。
- 如果对象太大,或者你要显式地控制对象的生存期,那就在堆上创建,其它情况基本可以选在栈上分配。
- 在栈上创建速度很快而且全程自动化,而在堆上创建会稍微繁琐,而且还有一个巨大的风险,即如果你忘记delete,有可能会造成内存泄漏。
当然你也可以使用智能指针,它基本集合了两种方法的优势,超出作用域还会被自动删除,还是比较好用的,之后的系列中应该会讲到。
测试代码
#include <iostream>class Animal
{
private:std::string m_name; int m_age;double m_weight;
public:void setName(std::string pname) {m_name = pname;}void getName(){std::cout << m_name << std::endl;}
};int main()
{Animal animal;animal.setName("Dog");animal.getName();Animal *animal1 = new Animal;animal1->setName("Cat");animal1->getName();delete animal1;return 0;
}