类与对象之动态内存分配
为什么我们需要动态内存分配
在前面的例子中,类中的成员都是在创建类的时候已经静态分配好空间的所以如果我们给这个成员赋超过这个空间大小的值,那么数据的存储将不完整。当然一种解决方法是将尽量大的空间分配给每个成员,但是这样会造成资源的浪费,最好的办法就是在程序运行时,在类的构造函数中使用new操作符来根据需求分配空间,此时我们的析构函数就不再是可有可无的函数,它必须正确的使用delete来释放内存,并且正确的与构造函数匹配
隐式成员函数
但是对于我们初学者来说,在类中使用new 和 delete 操作符是很有风险的,因为很多我们自以为没有问题的设计却隐藏了危机。
而很多问题都是自动定义的隐式成员函数引起的
C++会自动提供以下的成员函数:
1.默认构造函数
2.复制构造函数
3.赋值操作符
4.默认析构函数
5.地址操作符——返回调用对象的地址(this指针的值)
复制构造函数
复制构造函数用于将一个对象复制到新创建的对象中。
它只用于初始化
它的函数原型通常如下:
class_name (const class_name&);
何时调用复制构造函数
当我们新创建一个对象并将其初始化为同类现存对象时,复制构造函数将被调用。
最常见的就是将新对象显式地初始化为现有的对象
下面四种声明都将调用复制构造函数:
class Dog
{……};
Dog mike; # 假设mike是创建好的类
1. Dog scout(mike);
2. Dog scout = mike;
3. Dog scout = Dog(mike);
4. Dog *scout = new Dog(mike)
当函数按值传递对象或函数返回对象时,都会使用复制构造函数。
牢记:按值传递就是创建原始变量的一个副本!
复制构造函数的功能
一般来说默认的复制构造函数将非static成员都复制一般赋给新的对象
这里要介绍两种复制:
浅复制:仅仅复制其值,如果复制一个指针,则复制指针的地址
深度复制:复制所指向的内容,比如指针所指向值,而不是指针的地址
当我们创建的类中,一些成员是使用new初始化指向数据的指针,而不是数据时,我们需要显式定义复制构造函数
赋值操作符
我们能够实现类对象赋值,就是通过自动为类重载赋值操作符实现的
它的原型:
class_name & class_name::operator=(const class_name&);
它接受并返回一个指向类对象的引用
什么时候使用赋值操作符
- 将已存在的对象赋给另一个对象时
- 初始化对象时,不一定会使用赋值操作符(可能使用复制构造函数)
赋值操作符的功能
基本与复制构造函数相同
使用new和delete的注意事项:
1.构造函数中使用new来初始化指针对象,则应该在析构函数中使用delete
2.new 对应delete, new[ ]对应 delete[ ]
3.有多个构造函数时,唯一的析构函数必须与多个构造函数都兼容
总结
当对象包含指向new分配的内存的指针成员,将一个对象初始化或赋值为另一个存在的对象时,由于默认的复制构造函数是浅复制,所以两个对象的成员(指针)会指向同一个数据块,当最终调用析构函数时,系统将会尝试删除同一个数据块两次,这将出错
解决方法: 定义一个显式的复制构造函数来重新定义初始化,并重载赋值操作符。新的定义中,将创建指向数据的副本,并使新对象指向这些副本,这样两个对象将引用独立的,相同的数据,就不会出现重复删除的错误
点个赞呗!