文章目录
1、空类
- 如果一个类中什么成员都没有,简称为空类。空类并不是什么都没有,而会生成6个默认成员函数
- 析构函数完成初始化和清理工作
- 拷贝构造是使用同类对象对象初始化构建对象
- 赋值重载把一个对象赋值给另一个对象
- 取地址重载:普通对象和const对象取地址
2、构造函数
- 构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次。
- 构造函数并不是开辟空间创建对象,而是初始化对象。
// 构造函数
#include <iostream>
using namespace std;
class Date
{
public:
void SetDate(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Display()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.SetDate(2018, 5, 1);
d1.Display();
return 0;
}
2.1 构造函数特性
- 函数名与类名相同
- 无返回值
- 对象实例化编译器自动调用对应的构造函数
- 可以重载
2.2 构造函数的重载
#include <iostream>
using namespace std;
class Date
{
public:
Date()
{}
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1; // 调用无参构造函数
Date d2(2015, 1, 1); // 调用带参构造函数
return 0;
}
2.3 构造函数默认生成
如果类中没有显示定义构造函数,则C++ 会自动生成一个无参的默认构造函数,一旦显示定义编译器将不再生成。
#include <iostream>
using namespace std;
class Date
{
public:
Date()
{}
private:
int _year;
int _month;
int _day;
};
int main()
{
// 没有定义构造函数,对象也可以创建成功
Date d1;
return 0;
}
2.4 无参的和全缺省的构造函数只用定义一个
否则不然会产生二义性。
// 无参和全缺省的二义性
class Date
{
public:
Date()
{
_year = 1900;
_month = 1;
_day = 1;
}
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = _day;
}
private:
int _year;
int _month;
int _day;
};
2.5 内置类型和自定义类型
- 内置类型就是语法一定定义好的类型,如int, char
- 自定义类型:class / struct / union
2.6 自定义构造函数和默认的构造函数的区别
#include <iostream>
using namespace std;
class Time
{
public:
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year;
int _month;
int _day;
// 自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}
3、析构函数
- 在对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
- 析构函数是在类名前面加上字符 ~
- 无参无返回值
- 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
- 对象声明周期结束时,C++编译系统自动调用析构函数。
// 显示定义析构函数
typedef int DataType;
class SeqList
{
public:
SeqList(int capacity = 10)
{
_pData = (DataType*)malloc(capacity * sizeof(DataType));
assert(_pData);
_size = 0;
_capacity = capacity;
}
~SeqList()
{
if (_pData)
{
free(_pData);
_pData = NULL;
_capacity = 0;
_size = 0;
}
}
private:
int* _pData;
size_t _size;
size_t _capacity;
};
4、拷贝构造函数
- 拷贝构造函数:单个形参且形参是类类型对象的引用,一般常用const修饰
- 拷贝构造函数是构造函数的一种重载
- 拷贝构造函数的参数只有一个且必须使用引用传参
- 以值得方式传参,在传参期间会生成一个临时对象(实参的拷贝),使用传值方式会引发无穷递归调用
4.1 浅拷贝
浅拷贝:默认的拷贝构造函数对象按内存存储按字节序完成拷贝。
// 系统默认生成的浅拷贝
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
return 0;
}
4.1 浅拷贝问题
多个对象共同使用同一份资源,在这些对象被销毁时,同一份资源被多次释放而引起的代码崩溃。
注意:当涉及到资源管理时,我们需要显示定义拷贝构造函数。
class String {
public:
String(const char* str = "jack")
{
_str = (char*)malloc(strlen(str) + 1);
strcpy(_str, str);
}
~String()
{
cout << "~String()" << endl;
free(_str);
}
private:
char* _str;
};
int main()
{
String s1("hello");
String s2(s1);
}
5、赋值运算符重载
5.1 运算符重载
- 为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有返回值类型,函数名字以及参数列表,其返回值类型与参数列表和普通的函数类似。
- 关键字:operator
- 函数模型:返回值类型 operator操作符(参数列表)
注意:
- 重载运算符必须有一个类类型或者枚举类型的操作数
- 用于内置类型的操作符
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(const Date& d2)
{
return (_year == d2._year && _month == d2._month && _day == d2._day);
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1(2018, 9, 26);
Date d2(2018, 9, 27);
cout << (d1 == d2) << endl;
}
5.2 赋值运算符重载
赋值运算符四点注意:
- 返回值是否为该类型的引用(return *this)
- 参数类型是否被const修饰
- 是否实例自身已有的内存
- 检测是否自己给自己赋值
一个类没有显式定义赋值运算符,编译器也会生成一个,对象按字节序的值拷贝。
class CMyString
{
public:
CMyString(char* m_pData = nullptr);
~CMyString();
// 赋值运算符重载
// 返回该类型的引用 const修饰
CMyString& operator=(const CMyString& str)
{
// 判断是不是给自己赋值
if(*this != &str)
{
// 利用strTemp结束时,自动调用析构函数清理实例自身的已有的内存
CMyString strTemp(str);
char* pTemp = strTemp.m_pData;
strTemp.m_pData = m_pData;
m_pData = pTemp;
}
return *this;
}
private:
char* m_pData;
};
6、取地址及const取地址操作符重载
class Date
{
public:
Date*operator&()
{
return this;
}
const Date* operator&()const
{
return this;
}
private:
int _year;
int _month;
int _day;
};
7、const成员
将const修饰的类成员函数称之为const成员函数。const修饰类成员函数,实际修饰成员函数隐含的this指针,该成员函数不能对类的任何成员进行修改操作。
class Date
{
public:
void Display()
{
cout << "Display()" << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl;
}
void Display()const
{
cout << "Display() const " << endl;
cout << "year:" << _year << endl;
cout << "month:" << _month << endl;
cout << "day:" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
void Test()
{
Date d1;
d1.Display();
const Date d2;
d2.Display();
}
7.1 const对象和const成员函数
当类中有函数的const和非const版本:
- const对象默认调用const成员函数,非const对象默认调用非const成员函数
当类中只有函数的const或非const版本:
- const对象只能访问const成员函数,非const对象可以访问任意成员函数