1、this指针、构造,析构回顾
this指针:
- 一个类有很多对象,每个对象独自的成员变量,共享一套成员方法;
- 成员方法,一经编译,方法的参数都会添加一个this指针,接收调用该方法的对象。
构造函数:
定义对象时,自动调用的;可以重载的;构造完成,对象产生了
析构函数:
不带参数,不能重载,只有一个析构函数;析构完成,对象就不存在了
.data上的对象 定义的时候构造,程序结束时析构
heap上的对象 new的时候构造 delete的时候析构
stack上的对象 进入函数到它定义的地方构造,出函数作用域析构
2、浅拷贝
- 没有提供任何构造函数的时候,会为你生成默认构造和默认析构,是空函数;
- 当你提供一个构造函数的时候,就不会提供默认的构造了 ;
- 如果我们没有提供拷贝构造,调用的是默认的拷贝构造函数-》是做直接内存的数据拷贝
- 在调用第二个析构函数的时候崩溃了
- 系统提供的默认的拷贝构造函数做的是内存的拷贝—浅拷贝
- s1的指针还是指向中间那块内存,但是中间那块内存已经被释放了,所以s1的这个指针已经成为了野指针。
- 因此,s2析构完到s1析构的时候,成了释放野指针的操作了,程序崩溃。
默认的拷贝构造函数如下:(浅拷贝)
我们应该做深拷贝:
- 对象的成员变量不仅仅要把值给它;
- 如果对象的指针指向了外部资源,你应该给这个对象也单独开辟一个外部资源,让新对象的指针指向它,每个对象的指针指向自己独有的外部资源,析构时互不干扰!
经验:
- 在oop编程时,对象的成员变量有指针,构造时,发现指针指向外部堆内存, 这个对象默认构造时,一定会出现浅拷贝的问题的;
- 此时就不能依赖编译器产生的默认拷贝构造函数(它只会进行直接内存数据拷贝),自己写拷贝构造函数!
3、深拷贝
我们应该做深拷贝:
- 对象的成员变量不仅仅要把值给它;
- 如果对象的指针指向了外部资源,你应该给这个对象也单独开辟一个外部资源,让新对象的指针指向它,每个对象的指针指向自己独有的外部资源,析构时互不干扰!
我们要自定义拷贝构造函数(深拷贝),因为对象的浅拷贝有问题。
SeqStack(const SeqStack& src)
{
cout << "SeqStack(const SeqStack &src)" << endl;
_pstack = new int[src._size];
for (int i = 0; i <= src._top; ++i)
{
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
}
这样,对象就是各自析构自己的外部资源了! 现在就没有问题了!
4、扩容的时候为什么要用for循环,不用memcpy?
- 因为在进行数据的拷贝的时候,我们把内存上的数据拷贝到另一块内存上;
- 如果内存上放的是整型数据,每一个整型不占用整型之外的资源,我们使用memcpy拷贝字节过来,是没有问题的。
- 但是,我们假设这个数组里面放的不是整型,而是对象,每一个对象都有指针,指向外部的资源,则这个数组里的对象的浅拷贝是有问题的,用memcpy只是把对象本身的内存拷贝一份,调用realloc也是如此(memcpy和realloc做的都是浅拷贝),造成指针指向的都是同一块外部资源,在析构的时候,这个外部资源要析构2次,造成程序崩溃。
- 除非明确拷贝的数据,没有占用外部资源,则可以使用memcpy方法或者realloc,否则我们一定要用for循环去解决问题。
5、赋值重载函数
- 赋值操作,s1,s2都是存在的对象。
- 我们现在没有给类提供赋值操作,系统产生默认的赋值函数,也是做直接的内存拷贝!会在第二次析构的时候出现问题!
当对象浅拷贝有问题的时候,不仅要定义拷贝构造函数,还需要定义赋值重载函数
void operator=(const SeqStack& src)
{
cout << "operator=" << endl;
if (this == &src)
return;
delete[]_pstack;
_pstack = new int[src._size];
for (int i = 0; i <= src._top; ++i)
{
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
}
6、代码汇总
#include <iostream>
using namespace std;
class SeqStack
{
public:
SeqStack(int size = 10)
{
cout << this << " SeqStack()" << endl;
_pstack = new int[size];
_top = -1;
_size = size;
}
SeqStack(const SeqStack& src)
{
cout << "SeqStack(const SeqStack &src)" << endl;
_pstack = new int[src._size];
for (int i = 0; i <= src._top; ++i)
{
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
}
void operator=(const SeqStack& src)
{
cout << "operator=" << endl;
if (this == &src)
return;
delete[]_pstack;
_pstack = new int[src._size];
for (int i = 0; i <= src._top; ++i)
{
_pstack[i] = src._pstack[i];
}
_top = src._top;
_size = src._size;
}
~SeqStack()
{
cout << this << " ~SeqStack()" << endl;
delete[]_pstack;
_pstack = nullptr;
}
void push(int val)
{
if (full())
resize();
_pstack[++_top] = val;
}
void pop()
{
if (empty())
return;
--_top;
}
int top()
{
return _pstack[_top];
}
bool empty() { return _top == -1; }
bool full() { return _top == _size - 1; }
private:
int* _pstack;
int _top;
int _size;
void resize()
{
int* ptmp = new int[_size * 2];
for (int i = 0; i < _size; ++i)
{
ptmp[i] = _pstack[i];
}
delete[]_pstack;
_pstack = ptmp;
_size *= 2;
}
};
int main()
{
SeqStack s;
SeqStack s1(10);
SeqStack s2 = s1;
s2 = s1;
return 0;
}
|