目录
- 引言
- 1. 构造函数
- 1.1概念
- 1.2 特性
- 2. 析构函数
- 2.1 概念
- 2.2 特性
- 3. 拷贝构造函数
- 3.1 概念
- 3.2特征
- 4. 赋值运算符重载
- 4.1 运算符重载
- 4.2 赋值运算符重载
- 4.3 前置++和后置++重载
- 5.日期类的实现
- 6.const成员
- 7.取地址及const取地址操作符重载
引言
当一个类既没有成员变量又没有成员函数时(也就是空类,没有成员),编译器又该如何面对?
此时的编译器会无动于衷?非也
编译器会自动生成6个默认成员函数:
用户没有显示实现,编译器会生成的成员函数成为默认成员函数
1. 构造函数
1.1概念
构造函数用于在创建对象时初始化对象的状态。构造函数的名称与类名相同,不返回任何值(包括void),因此它们没有返回类型。
构造函数在以下情况下被调用:
- 当创建对象时,编译器会自动调用该对象的构造函数来初始化对象。
- 在使用new关键字动态分配内存时,构造函数也会被调用。
- 当对象作为函数参数传递给函数时,如果需要,构造函数也会被调用。
如下,构造函数的调用与创建
class Date
{
public:
//void Init(int year=2025, int month=2, int day=26)
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day
<< endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date s;
s.Init(2024,2,26);
//s.Init();
s.Print();
return 0;
}
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证
每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
1.2 特性
构造函数的职责是初始化对象,而非开设空间来创建对象
特性:
- 函数名与类名一样
- 无返回值
- 对象实例化,自动调用对用的构造函数
- 构造函数可以重载
int main()
{
Date s; // 若没有写构造函数,自动调用无参构造函数
//s.Init(2024,2,26);
s.Init();
//s.Init; 错误的
s.Print();
return 0;
}
- 若类中并没有定义构造函数,那么编译器会自动生成一个**
无参
**的默认构造函数,当用户显示定义那么就不会自动生成
注意:那么自动生成的构造函数的作用是什么?
通过实验可以得出,当没有定义构造函数而创建对象,而自动调用构造函数,最后却和没有初始化的效果一样(自动调用后值为随机值)
C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类
型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型
如下,自动调用自定义类型的构造函数的作用
#include"iostream"
using namespace std;
class little
{
public:
little()
{
cout << "little" << endl;
_mount = 24;
cout << _mount << endl;
}
private:
int _mount = 80;
};
class Date
{
public:
void Print()
{
cout << _year << _month << _day << endl;
}
private:
//内置类型
int _year;
int _month;
int _day;
//自定义类型
little t;
};
int main()
{
Date s;
s.Print();
return 0;
}
- 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为
是默认构造函数。
2. 析构函数
2.1 概念
析构函数的概念:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
在对象销毁时自动被调用,用于清理对象所占用的资源、释放内存等操作。它的名称与类名相同,前面加上波浪号(~),不接受任何参数,也没有返回值(包括void)。
2.2 特性
- 定义析构函数时,是在类名前加上 " ~ "
- 无参数无返回类型
- 一个类只能有一个析构函数,若为显式定义,系统会自动生成默认的析构函数.(析构函数无法重载)
- 对象生命周期结束时,C++编译系统自动调用析构函数
//Date函数
class Date
{
public:
void Init(int year=2025, int month=2, int day=26)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day
<< endl;
}
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date s;
//s.Init(2024,2,26);
s.Init();
//s.Init; 错误的
s.Print();
return 0;
}
注意:关于编译器自动生成的析构函数,与构造函数的原理类似
原理便是嵌套类的自调用
include"iostream"
using namespace std;
class little
{
public:
little()
{
cout << "little" << endl;
_mount = 24;
cout << _mount << endl;
}
~little()
{
cout << "~littel()" << endl;
}
private:
int _mount = 80;
};
class Date
{
private:
//内置类型
int _year;
int _month;
int _day;
//自定义类型
little t;
};
int main()
{
Date s;
return 0;
}
类中若没有开辟空间,可以不写析构函数,相当于C语言中的临时变量,出了生命域便销毁了,但若有资源申请,一定要写析构函数的,就好比C中开辟了空间最后要主动释放空间.
3. 拷贝构造函数
3.1 概念
只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰)
用于创建一个新对象并初始化为同一类的现有对象的副本。它的作用是用一个已存在的对象来初始化一个新创建的对象。
常用情况:
- 将一个对象作为参数传递给函数,以值传递的方式,或者以按值返回的方式。
- 在函数中创建一个新对象,并将其初始化为另一个对象的副本。
- 当对象被复制给另一个对象时,如对象的赋值操作或对象的初始化。
3.2特征
特殊成员函数之一
- 拷贝构造函数是构造函数的一个重载形式
- 参数只有一个且必须是"类"类型对象的**
引用
**,(注意:使用传值方式编译器直接报错,因为在传值过去,会自动触发调用函数,而进入无穷递归)
class Date
{
public:
//Date(const Date d ) 错误的写法
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date(int year=2025, int month=2, int day=26)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day
<< endl;
}
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date s(2024,2,27);
Date m(s);
s.Print();
m.Print();
return 0;
}
若以传值方式,造成无穷递归:
- 若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。如:类中成员需要开辟空间,此时生成的拷贝构造函数会拷贝这片空间,而不是开辟空间.
注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的
注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。
4. 赋值运算符重载
4.1 运算符重载
运算符重载是具有特殊函数名的函数
运算符重载的语法:
return_typefed operator symbol(parameter list)
{
//操作实现
}
class Date
{
public:
Date(int year=2025, int month=2, int day=26)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(const Date& d)
{
return _year == d._year &&
_month == d._month &&
_day == d._day;
}
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d;
Date s(2025, 2, 26);
cout << (s == d) << endl;
return 0;
}
运算符重载的一些注意事项:
1. 不能创建新的运算符,只能重载已有的运算符。
2. 不能改变运算符的优先级和结合性。
3. 一些运算符(如.和::)无法被重载。
4. 不能重载的运算符包括:.(成员访问运算符)、.*(成员指针访问运算符)、::(作用域解析运算符)、?:(条件运算符)、sizeof(大小运算符)、typeid(类型信息运算符)等。
4.2 赋值运算符重载
1.赋值运算符重载格式:
- 参数类型:const T&,传递引用提高效率
- 返回值类型:T&,返回引用可以提高返回得效率
- 检测是否自己 给自己赋值
- 返回*this:复合连续赋值的含义
class Date
{
public:
//Date(const Date d ) 错误的写法
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
Date(int year=2025, int month=2, int day=26)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day
<< endl;
}
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this; //解引用
}
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d(2024, 2, 27);
Date s = d;
s.Print();
d.Print();
return 0;
}
2.赋值运算符只能重载成类的成员函数不能重载成全局函数
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
4.3 前置++和后置++重载
class Date{
public:
//前置++:先加1再返回
// 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率
Date& operator++()
{
_day += 1;
return *this;
}
//后置++:先返回再加1
//C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器
//自动传递
//由于temp为临时对象,故不用引用
Date operator++(int)
{
Date temp(*this);
_day += 1;
return temp;
}
private:
//…………
}
5.日期类的实现
日期类函数实现
6.const成员
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
class Date
{
public:
Date(int year=2025, int month=2, int day=26)
{
_year = year;
_month = month;
_day = day;
}
void Print()const
{
cout << "Print()const" << endl;
cout << _year << "-" << _month << "-" << _day
<< endl;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day
<< endl;
}
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d(2024,2,27);
d.Print();
const Date s(2024, 2, 27);
s.Print();
return 0;
}
注意: const Date 变量时,不能去调用非const的成员函数,而 Date 变量可以去调用const 的成员函数
原理是:加上const 变量的权限被缩小,权限小的无法访问更高级权限的成员函数,而没有const的变量,去访问有const的成员函数,是高权限访问低权限
7.取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义 ,编译器默认会生成。
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
只有特殊需求,刻意修改取地址操作符,比如此类的地址不外露