9.1 复制构造函数
例:
自定义string类,以简化字符串的操作
注:
如果不重载赋值运算符,对象str1的内容直接复制到新对象str2中,对于没有指针的简单类来说这足够了,但当我们拥有作为数据成员的指针时,逐字节的复制将会把指针从一个对象复制给另一个对象,而两个指针就会同时指向一个内存
解决方案:重载赋值运算符
注:
- 当重载赋值运算符时,务必确定将一个对象中的所有数据成员都复制到另一个对象中
- 如果包含多个数据成员,则每个成员都需要复制到目标对象中
另一个场景:同一个类的对象初始化另一个对象时。
String str2(str1);
解决方案:为类定义赋值 / 复制构造函数(即以对象为参数的构造函数)
//使用const关键字可以保证复制过程中不会改变被复制的对象:
String(const String & str);//复制构造 / 拷贝构造函数
注:在下面三种场景里面会调用复制的构造函数:
- 当类的对象被初始化为同一类的另一个对象时
- 当对象被作为参数传递给一个函数时
- 当函数返回一个对象时
//自定义的字符串包装类
class String
{
public:
String();
String(char * str);
String(const String & str);//复制构造 / 拷贝构造函数
virtual ~String();
//重载赋值运算符,将数组中每个元素都进行复制,而不是只复制数组指针
const String & operator=(const String & str);
friend ostream & operator << (ostream & out, const String & str);
protected:
private:
int m_length; //字符串的实际长度,不包括\0
char * m_value; //实际存储字符的字符数组
};
String::String(): m_length(0)
{
//char * str = ""; //长度为0,但是实际的字符数组中会存在唯一的元素:\0
this->m_value = new char[m_length + 1];
this->m_value[0] = '\0';
}
ostream & operator << (ostream & out, const String & str)
{
out << str.m_value << "\n";
//out << "m_value的长度:" << strlen(str.m_value);
printf("%p", str.m_value);
return out;
}
String::String(char * str)
{
//将传入的字符串str的值赋给当前对象中的m_value
if(NULL == str){
this->m_value = new char[1];
this->m_value[0] = '\0';
return;
}
m_length = strlen(str);//获得复制字符串的长度
m_value = new char[m_length + 1];//为\0留出一个空间
strcpy(m_value, str);
}
String::String(const String & str)//复制构造 / 拷贝构造函数
{
m_length = strlen(str.m_value);
m_value = new char[m_length + 1];
strcpy(m_value, str.m_value);
}
//当重载赋值运算符,务必确定将一个对象中的所有数据都复制到另一个对象中(特别是有指针时)
//如果是包含多个成员,那么每个成员都需要复制到内存对象中 - 深复制
/* 如果一个成员拥有指针类型成员,那么大部分情况下,都需要使用深复制 - 才能将指针指向的内容复制一份出来,
让原有的对象和新对象相互独立*/
//如果类的成员没有指针,那么一般使用浅复制
const String & String::operator=(const String & str)
{
if(this == &str) return *this;
delete[] m_value; //首先要释放字符串的原始空间
m_length = str.m_length;
m_value = new char[m_length + 1];
strcpy(m_value, str.m_value);
return *this;
}
String::~String()
{
//析构时,释放字符数组所指向的空间
delete[] m_value;
}
void TestString()
{
String str1("我是你爹");
//String str2 = "sadwea";
String str2(str1);
cout << str1 << endl;
cout << str2 << endl;
cout << "对象之间的赋值" << endl;
str2 = str1;
cout << str1 << endl;
cout << str2 << endl;
}
·
·
·
·
·
自定义数组类:
//自定义的数组类
class Array
{
public:
Array(int length = 0);
Array(const Array & arr); //拷贝构造
virtual ~Array();
//int operator[](int index); //获取元素(无法写入)
const Array & operator=(const Array & arr);
int & operator[](int index);
friend ostream & operator<<(ostream & out, const Array & arr);
private:
int m_length;
int * m_datas;
};
Array::Array(int length): m_length(length)
{
if(m_length == 0){
m_datas = NULL;
}else{
m_datas = new int[m_length];
}
}
Array::Array(const Array & arr)
{
if(arr.m_length == 0) return;
m_length = arr.m_length;
m_datas = new int[m_length];
//1.使用循环组个元素赋值
//2.memcpy
memcpy(m_datas, arr.m_datas, m_length * sizeof(int));
}
const Array & Array ::operator=(const Array & arr)
{
if(this == &arr) return *this;
if(arr.m_length == 0) return *this;
m_length = arr.m_length;
m_datas = new int[m_length];
//1.使用循环组个元素赋值
//2.memcpy
memcpy(m_datas, arr.m_datas, m_length * sizeof(int));
return *this;
}
ostream & operator<<(ostream & out, const Array & arr)
{
for(int i = 0; i < arr.m_length; i++)
{
out << arr.m_datas[i] << ",";
}
out << endl;
}
/*
//重载中括号运算符
int Array::operator[](int index)
{
if(m_length == 0){
cerr << "数组为空,访问失败!" << endl;
}
if(index < 0 || index >= m_length){
cerr << "数组下标越界!" << endl;
}
return m_datas[index];
}*/
int & Array::operator[](int index)
{
if(m_length == 0){
cerr << "数组为空,访问失败!" << endl;
}
if(index < 0 || index >= m_length){
cerr << "数组下标越界!" << endl;
}
return m_datas[index];
}
Array::~Array()
{
delete[] m_datas;
}
//测试:
void TestArray()
{
Array arr1(10);
arr1[0] = 1234;
cout << arr1 << endl;
}
·
·
·
·
·
自定义容器类:
//使用了模板技术,模板技术一般用来做算法,比如重载100次某个类型的算法
//注:如果使用模板技术,那么类的声明和方法实现都要放在同一个头文件中
template<typename T>
class MyVector
{
public:
MyVector();
MyVector(int len, T element); //填充len长度的元素element
MyVector(const MyVector<T> & vec); //复制构造
virtual ~MyVector();
template<typename T2>
friend ostream & operator<<(ostream & out, const MyVector<T2> & vec);
MyVector<T> & operator=(const MyVector<T> & vec);
T & operator[](int index); //获取元素
void push_back(T element); //将元素element添加到内部数组中
/**
T pop_back(); // 返回并删除最后一个元素
void insert(int pos, T elemnt); //在pos位置处,插入元素element
void clear(); //清空所有的元素 **/
private:
T * m_elements;//用来存放元素的数组
int m_length;//所存放元素的实际个数
int m_capacity;//当前元素数组的大小vec.m_elements
};
template<typename T>
MyVector<T>::MyVector(): m_capacity(16), m_length(0)
{
this->m_elements = new T[m_capacity];
}
template<typename T>
MyVector<T>::MyVector(int len, T element): m_capacity(16)
{
m_capacity = len + m_capacity;
m_length = len;
m_elements = new T[m_capacity];
for(int i = 0; i < m_length; i++)
{ //1.使用for循环复制
//m_elements[i] = element; //每次都会调用重载的赋值运算符
//2.使用memcpy复制
memcpy(&m_elements[i], &element, sizeof(T));
}
}
template<typename T>
MyVector<T> & MyVector<T>::operator=(const MyVector<T> & vec)
{
if(this = &vec) return *this;
if(NULL != m_elements){
delete[] m_elements;
m_elements = NULL;
}
m_capacity = vec.m_length + vec.m_capacity;
m_length = vec.m_length;
m_elements = new T[vec.m_capacity];
memcpy(m_elements, vec.m_elements, m_length * sizeof(T));
return *this;
}
template<typename T>
void MyVector<T>::push_back(T element)
{
//将元素element添加到内部数组的最后一个位置
if(NULL == m_elements){
m_capacity = 16;
m_length = 0;
m_elements = new T[m_capacity];
}
//判断当前数组的容量是否已满
if(m_length == m_capacity){
//如果满了,就扩容(当前容量* + 1)
T * newElement = new T[m_capacity * 2 + 1];
//把原来的元素拷贝到新空间
memcpy(newElement, m_elements, m_length * sizeof(T));
delete m_elements; //释放空间
m_elements = newElement; //将元素数组指向修改为新空间
}
//m_elements[m_length++] = element;
memcpy(&m_elements[m_length++], &element, sizeof(T));
}
template<typename T>
T & MyVector<T>::operator[](int index)
{
return m_elements[index];
}
template<typename T2>
ostream & operator<<(ostream & out, const MyVector<T2> & vec)
{
for(int i = 0; i < vec.m_length; i++)
{
out << vec.m_elements[i] << ",";
}
out << endl;
return out;
}
//拷贝构造
template<typename T>
MyVector<T>::MyVector(const MyVector<T> & vec)
{
m_capacity = vec.m_capacity;
m_length = vec.m_length;
m_elements = new T[m_capacity];
memcpy(m_elements, vec.m_elements, m_length * sizeof(T));
}
template<typename T>
MyVector<T>::~MyVector()
{
delete[] m_elements;
}
//测试:
void TestVector()
{
MyVector<int> vec1; //默认构造
MyVector<double> vec2(10, 99.9);
cout << "vec1" << vec1 << endl;
cout << "vec2" << vec2 << endl;
MyVector<String> vec3(5, String("add"));
vec3.push_back("123");
cout << "vec3" << vec3 << endl;
}
9.2 类型的自动转换和强制转换
在C++中存在隐式类型转换语法(自动类型转换)
int a = 12;
a = 22.2 + a;
C++还提供了显式类型转换语法(强制转换)
类型名(变量)
int num = int(88.5);
C语言采用的语法格式
int num = (int)88.5;
注:
- 不管是自定类型转换还是强制类型转换,前提是编译器必须知道如何转换
① 将浮点型数据赋值给整型变量时,舍弃小数部分
② 将整型数据赋值给浮点型变量时,数值不变,但是以指数形式存储
③ 将double型数据赋值给float变量时,注意数值范围溢出
④ 字符型数据可以赋值给咱整型变量,此时存入的是字符的ASCLL码
等等…
9.2.1 自定义类型之间的转换
C++允许我们自定义类型转换规则:我们可以将其它类型转换为当前类类型,也可以将当前类类型转换为其它类型,这种自定义的类型转换规则只能以类的成员函数的形式出现
转换构造:将其他类型转换为当前类型时调用
class Rectange
{
public:
Rectange();
Rectange(float width, float height); //带参构造
Rectange(const Rectange & rect); //拷贝构造
Rectange(float width); //转换构造(将其它类型转换为当前类型时使用
void diaplay(){
cout << "width:" << width << endl;
cout << "height:" << height <<endl;
}
/*
//将几个构造混合在一起的写法:
Rectange(float width = 0, float height = 0): width(width), height(height)
{
}
*/
virtual ~Rectange();
private:
float width;
float height;
};
9.2.2当前类型转换为其他类型
类型转化函数:
类型转换函数的作用就是将当前类类型转换为其它类型;它只能以成员函数的形式出现(直接在.h文件中书写),也就是只能出现在类中。
//类型转换函数的语法格式:
operator type()
{
return data;
}
特点:
- 类型转换函数看起来没有返回值类型,其实是隐式地指明了返回值类型
- 类型转换函数也没有参数,因为要将当前类的对象转换为其它类型(实际上编译器会把当前对象的地址赋值给this指针,这样在函数体内就可以操作当前对象了)
operator float() const{//将矩形转换成float类型
return width;
}
operator Circle() const{//将矩形转换成Circle类型
return Circle(width / 2);
}
注:
- type可以是内置类型,类类型以及由typedef定义的数据别名,任何作为函数返回类型都是被支持的(不允许转换为数组或函数类型,可以转换为指针或引用类型)
- 类型转换函数一般不会更改被转换的对象,所以通常被定义为const
- 类型转换函数可以被继承,可以是虚函数