目录
(1)vector.h文件中用到cout,但没包头文件#include可以吗?——可以
(2)T()临时对象,C++中内置类型也可以认为有构造函数 析构函数
一.注意事项:
(1)vector.h文件中用到cout,但没包头文件#include<iostream>可以吗?——可以
vector.h文件中有cout输出流,( 例如vector.h文件中的void test_vector1() ),没包含头文件也没关系,.h文件不会编译,只会在.cpp文件展开,展开后向上查找头文件,找到了就可以正常运行,即:只要展开位置上方包含<iostream>即可运行。
(2)T()临时对象,C++中内置类型也可以认为有构造函数 析构函数
// C++中内置类型也可以认为有构造函数 析构函数
// 这样才能更好支持模板
// void resize(size_t n, T val = T())
///T()临时对象,自定义类型调用构造函数,有模板后,内置类型也有构造函数,T是int——>T()就是0,
//T是double——>T()就是0.0,T是int*——>T()就是NULL,
int i = 0;
int j = int();
int k = int(1);
(3)迭代器失效
如果VS中迭代器失效了,vs会进行检查,失效了就不能访问了,就会崩溃;Linux下会发生越界,也会崩溃。
①insert函数中迭代器pos失效(野指针)
针对size_t n = pos - _start; pos = _start + n;这2句
因为扩容时reserve会更新_start,_finish,_endofstoage到新的空间,但不会更新pos,pos仍指向旧空间,因此需要先用n算好扩容前原空间的pos和_start的距离,扩容后更新pos,让pos= _start + n; 即可找到在新空间的相对位置
(vector的插入操作如果导致扩容,则迭代器就会失效,但容量很多的前提下插入数据,迭代器不一定会失效。)
iterator insert(iterator pos, const T& x)
{
// 检查参数
assert(pos >= _start && pos <= _finish);
// 扩容
// 扩容以后pos就失效了,需要更新一下
if (_finish == _endofstoage)
{
size_t n = pos - _start; //先算好扩容前原空间的pos和_start的距离
size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
pos = _start + n; //扩容后pos仍指向原空间,更新pos
}
// 挪动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
②insert传参是值拷贝导致的it迭代器失效(野指针)
针对 it = v.insert(it, 20); 这一句
(假如要满足一个要求: 在所有的偶数的前面插入20。)
insert传参是值拷贝,如果insert函数无返回值(如果是下图的写法),那扩容后it还是指向旧空间,所以需要insert函数返回值返回更新后的 it 。
给引用可以吗?——不可以:解释: 若iterator insert(iterator pos, const T& x))中是引用iterator& pos 不就可以更新pos了吗?——是可以更新pos了,但有些情况下就会出问题,比如:v.insert(v.begin(),20); v.begin()调用的是函数begin(),这个函数begin()返回值是迭代器临时变量,临时变量具有常性,const传给非const是权限的放大,并且临时变量本身不能改变,传给pos后也不能更新pos。
void test_vector3()
{
// 在所有的偶数的前面插入20
vector<int> v;
//v.reserve(10);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.insert(it, 20); //如果insert函数无返回值,那扩容
//后it还是指向旧空间
++it;
}
++it;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
③ it指向位置意义改变也称为迭代器失效(迭代器意义变了)
(假如要满足一个要求: 在所有的偶数的前面插入20。)
每次insert后,++it都会再次指向值是2的位置,就导致it一直指向偶数2,就会一直插入,这种情况认为it也失效了,insert以后虽然没有扩容,it没有成为野指针,但是it指向位置意义变了。导致我们
这个程序重复插入20。
④erase如果缩容导致的迭代器失效
erase如果缩容开辟新空间就会导致
1、erase的失效都是意义变了。或者不在有效访问数据有效范围。
2、一般不会使用缩容的方案,那么erase的失效,一般也不存在野指针的失效
一般vector删除数据, 都不考虑缩容的方案。
因为缩容方案是: size() < capacity()/2时,可以考虑.开一个size() 大小的空间,拷贝数据,释放旧空间。缩容方案本质是时间换空间。一般设计都不会考虑缩容,因为实际比较关注时间效率,不关注空间效率,因为现在硬件设备空间都比较大,空间存储也比较便宜。
阶段二(5)类中模板参数省略也没错,但是不推荐
//vector(const vector& v); 模板参数省略也没错,但是不推荐
vector(const vector<T>& v)
: _start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{
vector<T> tmp(v.begin(), v.end()); //这里vector<T> 写成vector 也没错,不推荐
swap(tmp);
}
但是放到类外不写<T>就不行
(6)size_t改为int,否则会发生错误匹配问题
//vector(size_t n, const T& val = T()) 这里size_t用int,详情看第(6)
vector(int n, const T& val = T())
: _start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
形参中的size_t必须改为int,否则会发生错误匹配问题,举例:
vector<int> v1(10, 2); 本应该调用重载①的,但是因为v1的两个参数都是int,重载①第一个类型是size_t=unsigned int,还需要类型转换,v1干脆不匹配①而错误的匹配重载②的模板类型了,毕竟模板类型都是一样的,所以v1会匹配那个更适合的;把 size_t 改成 int 后就会更适合v1去优先匹配。
vector<char> v2(10, 'x'); 因为参数类型一个int一个char,所以不可能去匹配重载②(因为重载②两个参数都是模板类型,两个参数类型一样),会直接匹配重载①
(7)深层深浅拷贝问题
深拷贝:是指拷贝对象的具体内容,深拷贝在计算机中开辟一块新的内存地址用于存放复制的对象。源数据改变不会影响复制的数据。
为什么要使用深拷贝?
我们希望在改变新的数组(对象)的时候,不改变原数组(对象)
vector<T>中,当T是涉及深浅拷贝的类型时,如: string/vector<T>等等 ,我们扩容使用memcpy拷贝数据是存在浅拷贝问题
void reserve(size_t n)
{
size_t sz = size();
if (n > capacity())
{
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, size()*sizeof(T));
//当T是vector/string时防止memcpy浅拷贝的解决方案
for (size_t i = 0; i < size(); ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
}
_finish = _start + sz;
_endofstoage = _start + n;
}
当T是vector时,例如杨辉三角的vector<vector<int>>,memcpy(tmp, _start, size()*sizeof(T));只是把vector<vector<int>>中的每一个vector<int>的所有迭代器_start,_finish,_endofstoage拷贝一遍,他们指向的内容还是旧空间_start,当delete[] _start后,_star中的每一个vector<int>的所有迭代器_start,_finish,_endofstoaget指向资源被释放,但是tmp中的那些迭代器还是指向旧空间,就成了野指针,所以我们不能用memcpy,而是一个一个把每一个vector<int>单独赋值
(8)构造函数重载和拷贝构造中初始化列表的意义
构造函数重载中初始化列表的意义:构造函数重载写 初始化列表 去初始化this,因为此函数中的push_back需要依赖初始化列表初始化,比如push_back中的扩容问题:如果_finish=_endofstorage成立,即容量满了,需要看capacity() == 0 ? 4 : capacity() * 2;,capacity()=_endofstoage - _start,所以如果不初始化_endofstoage,_start为nullptr,那么就无法扩容。
拷贝构造中初始化列表的意义:同上,此函数中的 reserve 和 push_back 需要依赖初始化列表初始化。
template <class InputIterator> //构造函数重载
vector(InputIterator first, InputIterator last)
: _start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr) //这里写初始化列表初始化this,原因详见(8)
{
while (first != last)
{
push_back(*first);
++first;
}
}
vector(int n, const T& val = T()) //拷贝构造函数
: _start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr) //这里写初始化列表初始化this,原因详见(8)
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
一.vector模拟实现阶段一
内容:4个迭代器函数,最基本的构造函数,size(),capacity(),reserve,resize,push_back,pop_back,2个operator[] ,insert,erase,clear
vector.h
#pragma once
#include <assert.h>
// 模拟实现 -- 加深对这个容器理解,不是为了造更好的轮子
namespace bit
{
template<class T>
class vector
{
public:
typedef T* iterator; //易错【1】.忘写在public中!!!!!!
typedef const T* const_iterator;
vector()
:_start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstoage - _start;
}
void reserve(size_t n)
{
size_t sz = size();
if (n > capacity())
{
T* tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, size()*sizeof(T));
delete[] _start;
}
_start = tmp;
}
_finish = _start + sz;
_endofstoage = _start + n;
}
//void resize(size_t n, const T& val = T()) 这样也可以,如果编译器严格,
//这个编不过就写下面无const和&的写法
//T()临时对象,自定义类型调用构造函数,有模板后,内置类型也有构造函数,T是int——>T()就是0,
//T是double——>T()就是0.0,T是int*——>T()就是NULL,
void resize(size_t n, T val = T()) //这样也可以
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
void push_back(const T& x)
{
if (_finish == _endofstoage)
{
size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
}
*_finish = x;
++_finish;
//insert(end(), x);
}
void pop_back()
{
if (_finish > _start) //【2】.容易忘写!!!!!!
{
--_finish;
}
//erase(end()-1);
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
iterator insert(iterator pos, const T& x)//返回值是支持连续插入
//如果insert函数无返回值,那扩容后it(实参it传给pos)还是指向旧空间,详情见(3)②
{
// 检查参数
assert(pos >= _start && pos <= _finish);
// 扩容
// 扩容以后pos就失效了,需要更新一下
if (_finish == _endofstoage)
{
size_t n = pos - _start; //先算好扩容前原空间的pos和_start的距离
size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
pos = _start + n; //扩容后pos仍指向原空间,更新pos
}
// 挪动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
iterator erase(iterator pos) //在test_vector4()中
{
assert(pos >= _start && pos < _finish);
iterator it = pos + 1;
while (it != _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
void clear()
{
_finish = _start;
}
private:
iterator _start;
iterator _finish;
iterator _endofstoage;
};
void test_vector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
v.pop_back();
v.pop_back();
v.pop_back();
for (size_t i = 0; i < v.size(); ++i)
{
cout << v[i] << " ";
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test_vector2()
{
/*vector<int> v;
v.resize(10, -1);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;*/
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//v.push_back(5);
v.insert(v.begin(), 0);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test_vector3()
{
// 在所有的偶数的前面插入2
vector<int> v;
//v.reserve(10);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.insert(it, 20);
++it;
}
++it;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
}
void test_vector4()
{
vector<int> v;
//v.reserve(10);
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(4);
v.push_back(5);
cout << v.size() << ":" << v.capacity() << endl;
auto pos = find(v.begin(), v.end(), 4);
//std::find
//InputIterator find (InputIterator first, InputIterator last, const T& val);
if (pos != v.end())
{
v.erase(pos);
}
cout << *pos << endl;
*pos = 10;
cout << *pos << endl << endl;
cout << v.size() << ":" << v.capacity() << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
}
test.cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
#include "vector.h"
int main()
{
bit::test_vector3();
return 0;
}
二.vector阶段二
增加的东西:构造函数的3个重载,拷贝构造函数,析构函数,运算符重载=,reserve中的memcpy深层深浅拷贝解决方案。void test_vector5()开始
#pragma once
#include <assert.h>
// 模拟实现 -- 加深对这个容器理解,不是为了造更好的轮子
namespace bit
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector() //构造函数原始1
:_start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{}
//并不一定用的是vector的迭代器去初始化,还有可能用string的迭代器,看void test_vector5()
//所以用模板类型,而不是直接 vector(iterator first, iterator last)
template <class InputIterator>
vector(InputIterator first, InputIterator last) //构造函数重载2
: _start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr) //这里写初始化列表初始化this,原因详见(8)
{
while (first != last)
{
push_back(*first);
++first;
}
}
//vector(size_t n, const T& val = T()) 这里size_t用int,详情看第(6)
vector(int n, const T& val = T()) //构造函数重载3
: _start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr) //这里写初始化列表初始化this,原因详见(8)
{
reserve(n);
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstoage, v._endofstoage);
}
// 休息11:37继续
//vector(const vector& v); 详见(5) 模板参数省略也没错,但是不推荐,
vector(const vector<T>& v) //拷贝构造函数
: _start(nullptr)
, _finish(nullptr)
, _endofstoage(nullptr)
{
vector<T> tmp(v.begin(), v.end());
swap(tmp);
}
//vector& operator=(vector v)
vector<T>& operator=(vector<T> v) //=运算符重载
{
swap(v);
return *this;
}
// 资源管理
~vector() //析构函数
{
if(_start)
{
delete[] _start;
_start = _finish = _endofstoage = nullptr;
}
}
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
return _endofstoage - _start;
}
void reserve(size_t n)
{
size_t sz = size();
if (n > capacity())
{
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, size()*sizeof(T));
//当T是vector/string时防止memcpy浅拷贝的解决方案
for (size_t i = 0; i < size(); ++i)
{
tmp[i] = _start[i];
}
delete[] _start;
}
_start = tmp;
}
_finish = _start + sz;
_endofstoage = _start + n;
}
//void resize(size_t n, const T& val = T())
//T()临时对象,自定义类型调用构造函数,有模板后,内置类型也有构造函数,T是int——>T()就是0,
//T是double——>T()就是0.0,T是int*——>T()就是NULL。
//void resize(size_t n, const T& val = T())
void resize(size_t n, T val = T())
{
if (n > capacity())
{
reserve(n);
}
if (n > size())
{
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
else
{
_finish = _start + n;
}
}
void push_back(const T& x)
{
/*if (_finish == _endofstoage)
{
size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
}
*_finish = x;
++_finish;*/
insert(end(), x);
}
void pop_back()
{
/*if (_finish > _start)
{
--_finish;
}*/
erase(end()-1);
}
T& operator[](size_t pos)
{
assert(pos < size());
return _start[pos];
}
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
iterator insert(iterator pos, const T& x)//返回值是支持连续插入
//如果insert函数无返回值,那扩容后it(实参it传给pos)还是指向旧空间,详情见(3)②
{
// 检查参数
assert(pos >= _start && pos <= _finish);
// 扩容
// 扩容以后pos就失效了,需要更新一下
if (_finish == _endofstoage)
{
size_t n = pos - _start; //先算好扩容前原空间的pos和_start的距离
size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
reserve(newCapacity);
pos = _start + n; //扩容后pos仍指向原空间,更新pos
}
// 挪动数据
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
return pos;
}
iterator erase(iterator pos)
{
assert(pos >= _start && pos < _finish);
iterator it = pos + 1;
while (it != _finish)
{
*(it - 1) = *it;
++it;
}
--_finish;
return pos;
}
void clear()
{
_finish = _start;
}
private:
iterator _start;
iterator _finish;
iterator _endofstoage;
};
//template<class T>
//vector<T>::vector(const vector& v)
// : _start(nullptr)
// , _finish(nullptr)
// , _endofstoage(nullptr)
//{
// vector tmp(v.begin(), v.end());
// swap(tmp);
//}
void test_vector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
v.pop_back();
v.pop_back();
v.pop_back();
for (size_t i = 0; i < v.size(); ++i)
{
cout << v[i] << " ";
}
cout << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
// C++中内置类型也可以认为有构造函数 析构函数
// 这样才能更好支持模板
// void resize(size_t n, T val = T())
int i = 0;
int j = int();
int k = int(1);
}
void test_vector2()
{
/*vector<int> v;
v.resize(10, -1);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;*/
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//v.push_back(5);
v.insert(v.begin(), 0);
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test_vector3()
{
// 在所有的偶数的前面插入2
vector<int> v;
//v.reserve(10);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.insert(it, 20);
++it;
}
++it;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test_vector4()
{
vector<int> v;
//v.reserve(10);
v.push_back(1);
v.push_back(2);
v.push_back(2);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(4);
v.push_back(5);
cout << v.size() << ":" << v.capacity() << endl;
auto pos = find(v.begin(), v.end(), 4);
if (pos != v.end())
{
v.erase(pos);
}
cout << *pos << endl;
*pos = 10;
cout << *pos << endl << endl;
cout << v.size() << ":" << v.capacity() << endl;
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
void test_vector5()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vector<int> v1(v.begin(), v.end());
std::string s("hello");
vector<int> v2(s.begin(), s.end());
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
vector<int> v3(v);
for (auto e : v3)
{
cout << e << " ";
}
cout << endl;
v1 = v2;
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
}
void test_vector6()
{
// 排除法
vector<int> v1(10, 2);
for (auto e : v1)
{
cout << e << " ";
}
cout << endl;
vector<char> v2(10, 'x');
for (auto e : v2)
{
cout << e << " ";
}
cout << endl;
}
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> vv;
vv.resize(numRows);
for (size_t i = 0; i < vv.size(); ++i)
{
// 杨辉三角,每行个数依次递增
vv[i].resize(i + 1, 0);
// 第一个和最后一个初始化成1
vv[i][0] = 1;
vv[i][vv[i].size() - 1] = 1;
}
for (size_t i = 0; i < vv.size(); ++i)
{
for (size_t j = 0; j < vv[i].size(); ++j)
{
if (vv[i][j] == 0)
{
// 中间位置等于上一行j-1 和 j个相加
vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];
}
}
}
for (size_t i = 0; i < vv.size(); ++i)
{
for (size_t j = 0; j < vv[i].size(); ++j)
{
cout << vv[i][j] << " ";
}
cout << endl;
}
cout << endl;
return vv;
}
};
void test_vector7()
{
vector<vector<int>> ret = Solution().generate(5);
for (size_t i = 0; i < ret.size(); ++i)
{
for (size_t j = 0; j < ret[i].size(); ++j)
{
cout << ret[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
}
三.深拷贝
c++的拷贝有两种:即 深拷贝和 浅拷贝
1.深拷贝和浅拷贝
在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的;但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象,所以,此时,必须采用深拷贝。
深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝。
2.深拷贝
深拷贝:是指拷贝对象的具体内容,深拷贝在计算机中开辟一块新的内存地址用于存放复制的对象。源数据改变不会影响复制的数据。
为什么要使用深拷贝?
我们希望在改变新的数组(对象)的时候,不改变原数组(对象)
例如:
c++的默认构造函数就是浅拷贝,浅拷贝就是简单的赋值操作,如果对象中没有其他的资源(如:堆,文件,系统资源等),则深拷贝和浅拷贝没有什么区别,如:下面这是浅拷贝,b=a调用了默认类中的拷贝构造函数,在这里使用深浅拷贝并没有任何区别。
class A
{
public:
A(int _data)
: data(_data)
{}
private:
int data;
};
int main()
{
A a(5);
A b = a; // 仅仅是数据成员之间的赋值
}
这里构造函数中有堆内存的分配的情况就不能浅拷贝:
class A
{
public:
A(int _size) //构造函数
: size(_size)
{
data = new int[size]; //假如其中有一段动态分配的内存
}
~A() // 析构函数
{
delete[] data;
}
private:
int* data;
int size;
};
int main()
{
A a(5);
A b = a;
return 0;
}
这里构造函数中有堆内存的分配,b=a也是调用的默认拷贝构造函数,这时会出现一个什么样的情况呢?
a和b指向了同一块内存空间,所以当调用析构函数的时候,内存空间会被析构两次,所以这将导致内存泄露或程序崩溃。
怎么解决呢?这时需要用到 深拷贝–自定义拷贝构造函数
class A
{
public:
A(int _size)
: size(_size)
{
data = new int[size];
}
A(const A& _A) : size(_A.size) //拷贝构造
{
data = new int[size]; // 深拷贝
}
~A()
{
delete[] data;
}
private:
int* data;
int size;
};
int main()
{
A a(5);
A b = a;
return 0;
}
这时我们的内存是什么样的情况呢:
a和b都被开辟一个内存空间,这样就没问题了