类的定义:
java
#include<iostream>
using namespace std;
//类的定义:用class关键字来定义,同时也可以用struct替换class,c++中也可以用struct来定义结构体
// 区别是:class默认类中没有权限修饰的成员为私有,而struct默认类中没有权限修饰的成员为公有
//类中可以定义:1.成员变量,2.成员函数
// 类的定义
class 类名 {
// class访问权限:默认为私有
public:
// 公开成员(外部可以直接访问)
//成员函数声明 / 定义;
private:
// 私有成员(只有类自己能访问,外部不能用)
//成员变量;
}; //注意:加分号
小知识:
1.size_t 是 C/C++ 里的一个标准无符号整数类型,专门用来表示大小、长度、下标、计数。
如:sizeof(...) 返回值就是 size_t;std::string::size()、vector::size() 都返回 size_t
2.声明和定义的区别:声明是一种承诺,但是还没做,定义就是把这件事给落实了。
案例:定义一个数据结构栈的类
java
//定义一个数据结构栈的类
class Stack {
//成员函数
public:
//在写函数时,我们可以在类中定义,也可以在类中声明在类外面定义,
// 如下:
void Init(size_t n) {//初始化
//...
}
//void Destory() {//销毁
// //...
//}
//在类中声明
void Destory();
void Push(int x) {//入栈
//...
}
void Pop() {//出栈
//...
}
//成员变量
private:
int* _a;
size_t _top;
size_t _capacity;//size_t 是 C/C++ 里的一个标准无符号整数类型
};
//在类外定义,销毁栈
void Stack::Destory() {
//...
}
class A {
};
int main() {
//类实例化对象
Stack s1;
//计算类的大小
cout << sizeof(s1) << endl;//12,并没有计算类中函数的大小,为什么呢?
//答:成员函数存在公共代码区(代码段),所有对象共用一份,不占对象的内存。原因:若一个类实
例化出N个对象,每个对象的成员变量都存不同的值,但调用的函数是同一个,如果每个对象都放成员函数,
而这些成员函数是一样的,会造成空间上的浪费。所以就是实例化对象的大小,就是计算类中成员变量大小
之和,并且要考虑内存对齐规则
A a1;
cout << sizeof(a1) << endl;//1,没有成员变量类的大小是1,为什么?
//答:一个字节不是为了存储数据,而是为了占位,记录对象的地址,保证每个对象有独立地址
return 0;
}

构造函数
构造函数是用来初始化对象的,在对象创建时,调用这个函数完成对象的初始化操作。
特征:
-
函数名与类名相同。
-
无返回值。
-
对象实例化时编译器自动调用对应的构造函数。
-
构造函数可以重载。
-
如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦 用户显式定义编译器将不再生成
6.写构造函数的作用:构造函数会对自定类型对象调用的它的默认构造函数,可以将对象中的成员变量初始化为我们所指定的值
初始化调用函数时
java
#include<iostream>
using namespace std;
//定义类
class Date {
public:
//初始化
void Init(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
}
void Print() {//没有传参数,是因为编译器在运行时隐含this指针,谁调用这个函数,this就指向谁,且this不能为空this是存在栈上的,它是一个形参,:void Print(Date * this)
cout << _year << "-" << _month << "-" << _day << endl;
//编译器处理为: cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main() {
//实例化类
Date d1;
d1.Init(2026, 4, 17);
d1.Print();//编译器处理为:d1.Print(&d1);
return 0;
}

使用构造函数初始化:我们自己写的构造函数
java
//定义类
//使用构造函数初始化对象
class Date {
public:
Date() {//无参构造
_year = 0;
_month = 1;
_day = 1;
}
Date(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 d1;
d1.Print();
Date d2(2026,4,17);
d2.Print();
return 0;
}

全缺省写法
java
//定义类
//使用构造函数初始化对象
class Date {
public:
//Date() {//无参构造
// _year = 0;
// _month = 1;
// _day = 1;
//}
//全缺省写法//不能与上面的无参构造同时存在,会存在歧义
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main() {
//实例化类
Date d1;
d1.Print();
Date d2(2026,4,17);
d2.Print();
return 0;
}

使用构造函数初始化:编译器自动生成的构造函数
java
//定义类
//使用构造函数初始化对象
class Date {
public:
//没有定义构造函数,编译器自动生成并调用构造函数
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main() {
//实例化类
Date d1;//会调用默认构造函数:1.自己写的无参构造,2.自己实现的全缺省构造函数,3.编译器自动生成的构造函数
d1.Print();
return 0;
}

析构函数
析构函数:对象生命周期到了以后,自动调用析构函数,完成对象里面的资源清理工作,不是完成对象的销毁工作
特征:
-
析构函数名是在类名前加上字符 ~。
-
无参数无返回值类型。
-
一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
-
对象生命周期结束时,C++编译系统系统自动调用析构函数。
5.如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏。
java
//定义类
//使用析构函数完成资源清理工作
class Date {
public:
//全缺省写法
Date(int year = 0, int month = 1, int day = 1) {
cout << "Date:" << this << endl;
_year = year;
_month = month;
_day = day;
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
//析构函数
~Date() {
cout << "~Date:" << this << endl;//this:表示当前对象的地址
}
private:
int _year;
int _month;
int _day;
};
//栈中:后进先出
int main() {
//实例化类
Date d1;
Date d2(2026,4,17);
return 0;
}

拷贝构造函数
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
特征:
-
拷贝构造函数是构造函数的一个重载函数。
-
拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
3.若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
拷贝构造:自己写的
java
//拷贝构造
class Date {
public:
//全缺省写法
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
//拷贝构造
//Date(const Date d) {// 错误写法:编译报错,会引发无穷递归 ,参数Date d = d1,又会调用拷贝构造停不下来。
Date(const Date& d){//正确写法,写成对d1的引用:Date& d = d1;
_year = d._year;
_month = d._month;
_day = d._day;
//d._day = _day;//加const可以避免写成这样,导致对原来的值进行修改,const表示使用传入的值,不会做任何修改
}
void Print() {
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main() {
//实例化类
Date d1(2026,4,17);
Date d2(d1);//拷贝构造,d2与d1相同,等同于:Date d2 = d1;
return 0;
}

拷贝构造:编译器默认生成的拷贝构造
java
//若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
class Time
{
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
}
Time(const Time& t)
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
cout << "Time::Time(const Time&)" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d1;
// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数
Date d2(d1);
return 0;
}

赋值运算符重载
运算符重载是具有特殊函数名的函数,也有返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
格式:返回值类型 operator操作符(参数列表)
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型参数
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this
5.'.*', ' :: ', 'sizeof', ' ?: ', ' . ' 注意以上5个运算符不能重载。
java
//赋值运算符重载
class Date {
public:
Date(int year = 1970, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
//==运算符重载
//编译器会解释成这样:bool operator==(Date* this, const Date& d2)
bool operator==(const Date& d2){
return _year == d2._year
&& _month == d2._month
&& _day == d2._day;
}
//>运算符重载
bool operator>(const Date& d2) {
if (_year > d2._year) {
return true;
}
else if (_year == d2._year && _month > d2._month) {
return true;
}
else if (_year == d2._year && _month == d2._month && _day > d2._day) {
return true;
}
return false;
}
private:
int _year;
int _month;
int _day;
};
//自定义类型是不能用运算符的,用的话就得重载运算符
int main() {
Date d1(2026, 4, 17);
Date d2(2026, 4, 17);
cout << (d1 == d2) << endl;
cout << (d1 > d2) << endl;
}
