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



