- 先来定义一个类
class A
{
};
- 经过编译器处理之后,类A不在为空,它会自动的生成六个默认的成员函数,即使这六个成员函数什么也不做。(这些成员函数当我们没有定义时,编译器会显示的调用)处理之后相当于:
class A
{
public:
A();//构造函数
A(const A& a);//拷贝构造函数
~A();//析构函数
A& operator=(const A& a);//赋值运算符重载
A* operator &();//取地址运算符重载
const A* operator &() const;//const修饰的取地址运算符重载
};
一、构造函数
1.概念
- 构造函数:一个特殊的成员函数,名字与类名相同,创建类类型对象的时候,由编译器自动调用,在对象的生命周期内只且调用一次,以保证每个数据成员都有一个合适的初始值。
2.构造函数的特性
- 函数名和类名相同;
- 没有返回值;
- 有初始化列表(可以不用);
- 新对象被创建,由编译器自动调用,且在对象的生命周期内仅调用一次;
- 构造函数可以重载,实参决定了调用哪个构造函数;
- 如果没有显示的调用时,编译器会提供一个默认的构造函数;
- 无参构造函数和带有缺省值的构造函数都认为是缺省构造函数,并且缺省构造函数只能有一个;
- 构造函数不能用const修饰(因为const修饰类的成员函数时,该函数不能修改成员变量,但是构造函数要修改类的成员变量,因此不可以由const修饰)
3.初始化列表
- 以冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在圆括号中的初始化式。
4.初始化顺序
- 数据成员在类中定义的顺序就是参数列表中的初始化顺序;
- 初始化列表仅用于初始化数据成员,并不指定这些数据成员的初始化顺序;
- 每个成员在初始化列表中只能出现一次;
- 尽量避免使用成员初始化成员,成员初始化顺序最好和成员的定义顺序保持一致。
类中包含以下成员必须要放在初始化列表中初始化:
(1)引用数据类型
(2)const数据类型
(3)类类型成员(该类没有缺省的构造函数)
5.默认构造函数
- 默认构造函数使用与成员变量初始化相同的规则初始化成员,对于内置类型和复合类型的成员,如组、指针,只对定义在全局定义作用于的对象初始化,对于局部作用域的内置和复合类型不作初始化。
6.构造函数的作用
- 构建对象
- 初始化对象
- 类型转换
7.explcit
- 用explicit修饰构造函数,抑制由构造函数定义的隐式转换,explicit关键字类内部的构建声明上,在类的定义体外部的定义上不再重复。
二、拷贝构造函数
1.概念
- 只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数称为拷贝构造函数。拷贝构造函数是特殊的构造函数,创建对象时使用已存在的同类对象来进行初始化,由编译器自动调用。
class CDate
{p
ublic:
CDate()
{}
CDate( const int year, const int month, const int day)
{
_iYear = year;
_iMonth = month;
_iDay = day;
} C
Date( const CDate& date){
_iYear = date._iYear;
_iMonth = date._iMonth;
_iDay = date._iDay;
}
private:
int _iYear;
int _iMonth;
int _iDay;
};
2.特征:
(1)它是构造函数的重载;
(2)它的参数必须使用同类型对象的引用传递;
(3)如果没有显示定义,系统会自动合成一个默认的拷贝构造函数。默认的拷贝构造函数会依次拷贝类的数据成员完成初始化。
3.使用场景
- 使用已存在的对象创建新的对象
CDate d1(1990, 1, 1);
CDate d2(d1);
- 传值方式作为函数的参数
void FunTest(const CDate date)
{}
- 传值方式作为函数的返回值
CDate FunTest()
{
CDate date;
return date;
}
三、析构函数
1.概念
- 与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类的一些资源清理和汕尾工作。
class CArray
{p
ublic:
CArray(size_t capacity)
: _capacity(capacity)
{
_pData = ( int*)malloc(capacity*sizeof (int));
_size = 0;
} ~
CArray()
{
if (NULL != _pData)
{
free(_pData);
_pData = NULL;
}_
size = 0;
_capacity = 0;
}
private:
int* _pData;
size_t _size;
size_t _capacity;
};
2.特性
- 析构函数在类名(即构造函数名)加上字符~。
- 析构函数无参数无返回值。
- 一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数。
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
- 注意析构函数体内并不是删除对象,而是做一些清理工作。
四、赋值运算符重载
1.概念
对于类类型的对象我们需要对‘=’重载,以完成类类型对象之间的赋值。
2.static成员
声明为static的类成员(成员数据或成员函数)称为类的静态成员。
- 特性:
(1)静态成员为所有类对象所共享,不属于某个具体的实例。
(2)类静态成员即可用类名::静态成员或者对象.静态成员来访问。
(3)类静态成员变量必须在类外定义,定义时不添加static关键字。
(4)类的静态成员函数没有默认的this指针,因此在它里面不能使用任何非静态成员。
(5)静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值,const修饰符等参数。
注意:
(1)静态成员函数不可以调用非静态成员函数;
(2)非静态的成员函数可以调用静态的成员函数。
3.const修饰类成员
使用场景:
(1)const修饰形参,一般和引用同时使用;
(2)const修饰返回值;
(3)const修饰类数据成员,必须在构造函数的初始化列表中初始化;
(4)const修饰类成员函数,实际修饰隐含的this,表示在类中不可以对类的任何成员进行修改;
(5)在const修饰的成员函数中要对类的某个数据成员进行修改,该数据成员定义声明是必须加mutable关键字。
说明:
const对象不可以调用非const成员函数,非const成员函数可以调用const成员函数
五、取地址操作符重载
class CTest
{
public:
CTest* operator&()
{
return this ;
}
const CTest* operator &()const
{
return this ;
}
};
取址操作符重载函数返回值为该类型的指针,无参数。
- inline函数
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
1> inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的的函数不适宜使用内联。
2> inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联
3> inline必须函数定义放在一起,才能成为内联函数,仅将inline放在声明前是不起作用的。
4> 定义在类内的成员函数默认定义为内联函数。
六、const修饰的取地址操作符重载
const String* operator&() const
{
return this;
}
函数后边的const表明在函数体中不能改变对象的成员,当然可以改变mutable变量。函数的返回值是指向常对象的指针。
练习题:编写String类的构造函数、析构函数、拷贝构造函数 、赋值运算符
- 已知String类的原型为:
class String
{
public:
String(const char* str = NULL);//普通构造函数
String(const String& other);//拷贝构造函数
~String(void);//析构函数
String& operator=(const String& other);//赋值函数
private:
char* m_data;//用于保存字符串
};
- 编写函数
//构造函数
String::String(const char* str)
{
if (str == NULL)
{
m_data = new char[1];
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length + 1];
strcpy(m_data, str);
}
}
//拷贝构造函数
String::String(const String& other)
{
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
}
//析构函数
String::~String(void)
{
delete[] m_data;
//由于m_data是内部成员,也可以写成delete m_data
}
//赋值运算符重载
String& String::operator=(const String& other)
{
//检查自赋值
if (this == &other)
return* this;
//释放原有的内存资源
delete[] m_data;
//分配新的内存资源,并复制内容
int length = strlen(other.m_data);
m_data = new char[length + 1];
strcpy(m_data, other.m_data);
//返回本对象的引用
return* this;
}