C++:类和对象---进阶篇

作者:仟千意日期:2025/11/8

1. 类的默认成员函数

默认成员函数就是我们没有显式实现,C++会自动生成的成员函数称为默认成员函数,C++11后,C++类的默认成员函数有8个默认构造函数、默认析构函数、拷贝构造函数、赋值运算符重载、取地址运算符重载、const取地址运算符重载、移动构造函数(C++11后)、移动赋值运算符重载(C++11后)),我们此文只了解重要的前4个,后4个中前两个不常用,后两个之后再做讲解。

2. 构造函数

构造函数是特殊的成员函数,虽名为构造,但它完成的是成员变量的初始化工作,所以它可以完美的替代Init函数。

构造函数的特点

1. 函数名与类名相同

2. 无返回值

3. 对象实例化时会自动调用构造函数

4. 构造函数可以重载

5. 没有显式定义构造函数,编译器会自动生成一个无参的默认构造参数,一旦显式定义后,将不再生成。

6. 无参构造函数、全缺省构造函数、我们不写编译器生成的构造函数都叫做默认构造函数。这三种默认构造函数,不能同时存在,无参和全缺省构造函数随构成函数重载,但存在调用歧义。

7. 我们不写,编译器默认生成的构造函数,对内置类型的变量处理没有要求,是否初始化是不确定的;对自定义类型成员变量,要求调用它的默认构造函数,如果没有默认构造函数,就会报错。我们要初始化这个成员变量,需要用到初始化列表,初始化列表我们放到第7点精讲。

1//默认构造函数
2using namespace std;
3class Date
4{
5public:
6	//// 1.⽆参构造函数
7	//Date()
8	//{
9	//	_year = 1;
10	//	_month = 1;
11	//	_day = 1;
12	//}
13	
14	//// 2.带参构造函数
15	//Date(int year, int month, int day)
16	//{
17	//	_year = year;
18	//	_month = month;
19	//	_day = day;
20	//}
21
22	// 3.全缺省构造函数
23	Date(int year = 1, int month = 1, int day = 1)
24	{
25	_year = year;
26	_month = month;
27	_day = day;
28	}
29
30	void Print()
31	{
32		cout << _year << "/" << _month << "/" << _day << endl;
33	}
34private:
35	int _year;
36	int _month;
37	int _day;
38};
39int main()
40{
41	Date d1; // 调⽤默认构造函数
42	Date d2(2025, 1, 1); // 调⽤带参的构造函数
43
44	Date d3();
45	d1.Print();
46	d2.Print();
47	return 0;
48}
49

3. 析构函数

析构函数与构造函数功能相反,不是对对象本身的销毁,而是对对象中资源的清理释放,局部对象存在于栈帧中,函数结束销毁栈帧,不需要析构函数处理,C++规定在对象销毁时会自动调用析构函数。析构函数的功能类似我们实现的栈中的Destory函数,日期类中没有资源需要释放,严格来说,不需要析构函数。

析构函数的特点

1. 析构函数名是类名加~

2. 无参数无返回值

3. 一个类只有一个析构函数,若未显式定义,则系统会默认析构函数

4. 对象生命周期结束时,会自动调用析构函数

5. 我们不写,编译器默认生成的析构函数,对内置类型成员不做处理,自定义类型成员会调用它们的析构函数(与构造函数类似)

6. 特别注意的是,即使我们显式写析构函数,对于自定义类型成员,编译器还是会调用它们的析构函数

1// 两个栈实现队列
2class MyQueue
3{
4public:
5	//编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源
6	// 显⽰写析构,也会⾃动调⽤Stack的析构 减少因析构函数错误而带来内存泄露的风险
7	~MyQueue()
8	{
9		cout << "~MyQueue" << endl;
10	}
11private:
12	Stack pushst;
13	Stack popst;
14};
15int main()
16{
17	//Stack st;
18	MyQueue mq;
19	return 0;
20}

7. 如果类中没有资源申请,可以不写析构函数,直接使用编译器生成的默认析构函数;如果默认的析构函数也可以用,也可以不写像~MyQueue(),但如果有资源申请,一定自己实现析构函数,否则会造成内存泄漏。

8. 一个局部域的多个对象,C++规定后定义的先析构

1//析构函数 ~Stack()
2typedef int STDataType;
3class Stack
4{
5public:
6	Stack(int n = 4)
7	{
8		_a = (STDataType*)malloc(sizeof(STDataType) * n);
9		if (nullptr == _a)
10		{
11			perror("malloc申请空间失败");
12			return;
13		}
14		_capacity = n;
15		_top = 0;
16	}
17	~Stack()
18	{
19		free(_a);
20		_a = nullptr;
21		_top = _capacity = 0;
22	}
23private:
24	STDataType* _a;
25	size_t _capacity;
26	size_t _top;
27};

4. 拷贝构造函数

如果一个构造函数第一个参数是自身类类型的引用,且任何额外的参数都有默认值,那个这个函数就是拷贝构造函数。所以拷贝构造函数是一个特殊的构造函数。

拷贝构造函数的特点

1. 拷贝构造函数是构造函数的重载

2. C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以自定义类型进行传值传参和传值返回都会调用拷贝构造

3. 拷贝构造函数第一个参数必须是类类型的引用,传值调用会报错(引发无穷递归调用),拷贝构造函数可以有多个参数,但第一个必须是类类型的引用,后面参数都有缺省值

4. 若未显式定义拷贝构造函数,编译器会自动生成拷贝构造函数,对于内置类型成员变量会完成浅拷贝(一个字节一个字节拷贝),对自定义类型成员变量会调用它的拷贝构造

5. 像Date类这样的全是内置类型且没有资源申请,编译器自动生成的拷贝构造就可以,不需要显式实现,像Stack这样的类,_a指向了资源,编译器自动生成的拷贝构造函数完成浅拷贝不符合需求,我们需要自己实现深拷贝(对指向资源也进行拷贝)

6. 传值返回会产生一个临时对象,调用拷贝构造函数,传值引用返回,没有拷贝。所以传值引用可以提高效率,但一定要保证传值引用返回时,一定要保证返回对象在函数结束后没有被销毁,否则会变成野引用。

1typedef int STDataType;
2class Stack
3{
4public:
5	Stack(int n = 4)
6	{
7		_a = (STDataType*)malloc(sizeof(STDataType) * n);
8		if (nullptr == _a)
9		{
10			perror("malloc申请空间失败");
11			return;
12		}
13		_capacity = n;
14		_top = 0;
15	}
16    Stack(const Stack& s)
17    {
18		if (nullptr == _a)
19		{
20			perror("malloc申请空间失败");
21			return;
22		}
23        _a = (STDataType*)malloc(sizeof(STDataType) * s._capacity);
24		_capacity = s._capacity;
25		_top = s._top;
26    }
27private:
28	STDataType* _a;
29	size_t _capacity;
30	size_t _top;
31};

5. 赋值运算符重载

5.1 运算符重载

当运算符被用于类类型的对象时,C++允许我们通过运算符重载的形式指定新的含义,C++规定类类型使用运算符时,必须转换成调用对应运算符重载,如果没有,就会报错。

运算符重载是具有特殊名字的函数,他的名字由operator和后面要定义的运算符共同组成,同样具有返回类型、参数列表和函数体。

重载运算符函数的参数和运算符作用的运算对象一样多,一元运算符有一个参数,二元运算符有两个参数,运算符左侧运算对象传给第一个参数,右侧运算对象传给第二个参数。

如果一个重载运算符函数是成员函数,第一个参数默认传给隐式的this指针,所以运算符重载作为成员函数时,参数比运算对象少一个。

运算符重载以后,其优先级和结合性与对应的内置运算符保持一致。

不能通过连接语法中没有的运算符来重载:如operator@。

.* :: sizeof ?: . 这五个运算符不能重载。(能重载的运算符必须有一个类类型对象参数,不能通过重载修改原来内置类型对象的含义)

重载++运算符时,有前置++和后置++之分,重载运算符名字都是operator++,无法区分,C++规定,使用后置++时,要传一个int形参,构成重载,以作区分。

重载<<和>>时,要重载为全局函数,因为重载为成员函数,隐式的this指针抢占了第一个参数,调用时就变成了对象<<cout,改变了原本习惯,也影响可读性。

5.2 赋值运算符重载

赋值运算符重载是一个默认成员函数,用于完成两个已存在对象的直接拷贝赋值,与拷贝构造不同的是,拷贝构造是一个对象拷贝初始化一个要创建的对象。

赋值运算符重载的特点

1. 赋值运算符重载(=)是一个运算符重载,规定必须重载为成员函数,其他复合型赋值运算符(+=、*=等等)推荐重载为成员函数,也可重载为全局。赋值运算符重载参数建议写为const当前类类型引用,可以减少拷贝。

2. 赋值运算符有返回值,建议写成当前类类型引用,减少拷贝,有返回值是为了支持连续赋值。

3. 没有显式实现时,编译器生成一个默认赋值运算符重载,对内置类型完成浅拷贝,自定义类型调用他们的赋值运算符重载。

4. 成员变量全是内置类型且没有指向资源时,不需要显式实现赋值运算符重载,有资源申请时,才需要显式实现赋值运算符重载函数

以判断两日期是否相等为例:

1Date& operator=(const Date& d)
2{
3    if (this != &d)
4    {
5    _year = d._year;
6    _month = d._month;
7    _day = d._day;
8}

6. 再探构造函数(*)

1. 前面我们实现构造函数,主要是使用函数体内赋值,构造函数还有一种初始化方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分割的数据成员列表,每个成员变量后面跟一个放在括号里的初始值或表达式。

2. 每个成员变量在初始化列表只能出现一次。语法上初始化列表可以认为是每个成员变量初始化定义的地方

3. 引用成员变量,const成员变量,没有默认构造的类类型变量必须在初始化列表初始化,否则报错。

4. C++11后支持在成员变量声明处给缺省值,主要给没有在初始化显式初始化的成员使用。

5. 尽量使用初始化列表初始化,因为即使不在初始化列表显式初始化,所以非静态成员变量也都会走初始化列表,未显式初始化的内置类型成员变量如果在声明处有缺省值,就用缺省值初始化,如果没有,初始化还是不初始化取决于编译器,没有规定;对于没有在初始化列表显式初始化的类类型成员变量,会调用它的默认构造函数,如果没有,就会报错。

6. 初始化列表初始化成员变量按照在类中的声明顺序初始化,跟初始化列表中的先后顺序无关,建议两者顺序一致。

1#include<iostream>
2using namespace std;
3class Time
4{
5public:
6	Time(int hour)
7		:_hour(hour)
8	{
9        //时分秒过程... 只是测试不做实现
10		cout << "Time()" << endl;
11	}
12private:
13	int _hour;
14};
15
16class Date
17{
18public:
19	Date(int& x, int year = 1, int month = 1, int day = 1)
20		: _t(12)
21		, _ref(x)
22		, _n(1)
23	{
24
25	}
26	void Print() const
27	{
28		cout << _year << "-" << _month << "-" << _day << endl;
29	}
30private:
31	int _year = 1900;
32	int _month = 1;
33	int _day = 1;
34	Time _t; // 没有默认构造
35	int& _ref; // 引⽤
36	const int _n; // const
37};
38int main()
39{
40	int i = 0;
41	Date d1(i);
42	d1.Print();
43	return 0;
44}

这里参数缺省值和成员变量声明缺省值做一个区分

参数缺省值是参数不传参使用缺省值做形参,不影响成员变量的初始化;而成员变量缺省值就是初始化列表不显式传参时使用来初始化成员变量的,二者没有关系。

7. 类型转换

C++支持内置类型隐式转换为其他类类型对象,需要有内置类型做参数的构造函数;

构造函数前加explicit就不再支持隐式类型转换;

C++还支持类类型之间相互转换,同样需要有类类型做参数的构造函数。

1#include<iostream>
2using namespace std;
3class A
4{
5public:
6    // 构造函数explicit就不再⽀持隐式类型转换
7    // explicit A(int a1)
8    A(int a1)
9    :_a1(a1)
10    {}
11
12    A(int a1, int a2)
13    :_a1(a1)
14    , _a2(a2)
15    {}
16    void Print()
17    {
18        cout << _a1 << " " << _a2 << endl;
19    }
20    int Get() const
21    {
22        return _a1 + _a2;
23    }
24private:
25    int _a1 = 1;
26    int _a2 = 2;
27};
28
29class B
30{
31public:
32    //支持类类型隐式转换的构造函数
33    B(const A& a)
34    :_b(a.Get())
35    {}
36private:
37    int _b = 0;
38};

8. static成员

用static修饰的成员变量称为静态成员变量,静态成员变量一定要在类外初始化(因为所有类对象共享同一块内存,不能通过初始化列表重复构造);

静态成员变量为所有类对象共享,不属于某个类对象中,存放于静态区;

用static修饰的成员函数称为静态成员函数,静态成员函数没有this指针

静态成员函数可以访问其他静态成员,但是不能访问非静态成员,因为没有this指针;

非静态成员可以访问任意的静态成员变量和静态成员函数;

突破类域就可以访问静态成员,可以通过类名::成员或对象::成员来访问静态成员变量和静态成员函数;

静态成员也是类的成员,受访问限定符的限制;

静态成员变量不能在声明处给缺省值,因为它不走初始化列表。

实现一个类 计算程序中创建出多少个类对象

1#include<iostream>
2using namespace std;
3class A
4{
5public:
6	A()
7	{
8		++_scount;
9	}
10	A(const A& t)
11	{
12		++_scount;
13	}
14	
15	~A()
16	{
17		--_scount;
18	}
19	static int GetACount()
20	{
21		return _scount;
22	}
23private:
24	// 类⾥⾯声明
25	static int _scount;
26};
27// 类外⾯初始化
28int A::_scount = 0;
29
30int main()
31{
32	cout << A::GetACount() << endl;
33	A a1, a2;
34	A a3(a1);
35	cout << A::GetACount() << endl;
36	cout << a1.GetACount() << endl;
37	return 0;
38}
39

牛客练习:1+2+3+...+n

链接:https://www.nowcoder.com/share/jump/6657127531762514146651

9. 友元

友元提供了一种突破访问限定符封装的方式,分为友元函数和友元类,在函数声明或者类声明的前面加friend,并把友元声明放到一个类内。

外部友元函数可以访问私有和保护成员,友元函数不是类的成员函数,只是声明我(友元函数)是你(类)的朋友,可以不用靠物质媒介(访问限定符)来找你;

友元函数声明可以类的任意地方,不受访问限定符限制,一个函数可以是多个类的友元函数;

友元类中的成员函数都是另一个类的友元函数,都可以访问其私有和保护成员;

友元类的关系是单向的,且不能传递(如果要互为友元,需要互相包含友元声明)

友元一定程度上提供了便利,但不宜多用,会破坏耦合度。

10. 内部类

如果一个类定义在另一个类的内部,那么这个类就叫做内部类。

内部类是独立的类,只是受类域和访问限定符的限制,所以外部类定义的对象不包含内部类;

内部类默认是外部类的友元类;

内部类本质也是一种封装,如果A类与B类有很大关联,可以把A变为内部类,如果只想给B类内部使用,可以用访问限定符限制。

1class A
2{
3private:
4	static int _k;
5	int _h = 1;
6public:
7	class B // B默认就是A的友元类
8	{
9	public:
10		void foo(const A& a)
11		{
12			cout << _k << endl;
13			cout << a._h << endl;
14		}
15		int _b1;
16	};
17};
18
19int A::_k = 1;
20
21int main()
22{
23	cout << sizeof(A) << endl; //输出4
24	A::B b; //内部类对象的创建
25	A aa;
26	b.foo(aa);
27	return 0;
28}

11. 匿名对象

用 ”类型 (实参)“ 定义出来的对象叫做匿名对象;相比之前用 ”类型 对象名(实参)“ 定义的对象叫做有名对象;匿名对象生命周期只在当前这一行,一般临时定义一个对象当前行用一下,就可以用匿名对象。

1class A
2{
3public:
4	A(int a = 0)
5		:_a(a)
6	{
7		cout << "A(int a)" << endl;
8	}
9	~A()
10	{
11		cout << "~A()" << endl;
12	}
13private:
14	int _a;
15};
16
17class Solution {
18public:
19	int Sum_Solution(int n) {
20		//...
21		return n;
22	}
23};
24
25int main()
26{
27	A();
28	A(1);//匿名对象在当前行结束时就调用析构函数
29	A aa2(2);
30
31	//匿名对象这样场景就很方便
32	Solution().Sum_Solution(10);
33	return 0;
34}

12. 对象拷贝时的编译器优化

现代编译器为了尽可能提高效率,在不影响正确性的情况下会尽可能减少传值和传返回值过程中的可以省略的拷贝;如何优化C++没有规定,当前主流编译器会对一行表达式中的连续拷贝进行优化,一些“激进”的编译器甚至会多行合并优化

一些常见的编译器优化:

1#include<iostream>
2using namespace std;
3class A
4{
5public:
6	A(int a = 0)
7		:_a1(a)
8	{
9		cout << "A(int a)" << endl;
10	}
11	A(const A& aa)
12		:_a1(aa._a1)
13	{
14		cout << "A(const A& aa)" << endl;
15	}
16	A& operator=(const A& aa)
17	{
18		cout << "A& operator=(const A& aa)" << endl;
19		if (this != &aa)
20		{
21			_a1 = aa._a1;
22		}
23		return *this;
24	}
25	~A()
26	{
27		cout << "~A()" << endl;
28	}
29private:
30	int _a1 = 1;
31};
32
33void f1(A aa)
34{}
35
36A f2()
37{
38	A aa;
39	return aa;
40}
41
42int main()
43{
44	// 传值传参
45	// 构造+拷⻉构造 无优化
46	A aa1;
47	f1(aa1);
48	cout << endl;
49
50	// 隐式类型,构造+拷⻉构造->省略临时对象 优化为直接构造
51    //(int->构造给临时对象->拷贝构造)
52	f1(1);
53	// ⼀个表达式中,构造+拷⻉构造->优化为直接构造
54	f1(A(2));
55	cout << endl;
56
57    //(构造->拷贝构造给临时对象)优化为直接构造临时对象返回
58	f2();
59	cout << endl;
60    //(构造->拷贝构造给临时对象返回->拷贝构造aa2)优化为直接构造aa2
61	A aa2 = f2();
62	cout << endl;
63    //(构造->拷贝构造给临时对象返回->赋值)优化为构造临时对象->赋值
64	aa1 = f2();
65	cout << endl;
66	
67	return 0;
68}

13. 日期类的实现 (*)

13.1 Date.h

1#pragma once
2#include<iostream>
3using namespace std;
4#include<assert.h>
5
6class Date
7{
8	friend ostream& operator<<(ostream& out, const Date& d);
9	friend istream& operator>>(istream& in, Date& d);
10
11public:
12	bool CheckDate() const;
13	Date(int year = 1900, int month = 1, int day = 1);
14	void Print() const;
15
16	
17	int GetMonthDay(int year, int month) const
18	{
19		assert(month >0 && month < 13);
20
21		static int MonthDayArrey[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
22
23		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
24		{
25			return 29;
26		}
27
28		return MonthDayArrey[month];
29	}
30
31	bool operator<(const Date& d) const;
32	bool operator<=(const Date& d) const;
33	bool operator>(const Date& d) const;
34	bool operator>=(const Date& d) const;
35	bool operator==(const Date& d) const;
36	bool operator!=(const Date& d) const;
37
38	Date operator+(int day) const;
39	Date& operator+=(int day);
40
41	Date operator-(int day) const;
42	Date& operator-=(int day);
43
44	// d1++;
45	// d1.operator++(0);
46	Date operator++(int);
47
48	// ++d1;
49	// d1.operator++();
50	Date& operator++();
51
52	// d1--;
53	// d1.operator--(0);
54	Date operator--(int);
55
56	// --d1;
57	// d1.operator--();
58	Date& operator--();
59
60	// d1 - d2
61	int operator-(const Date& d) const;
62
63
64private:
65	int _year;
66	int _month;
67	int _day;
68};
69
70ostream& operator<<(ostream& out, const Date& d);
71istream& operator>>(istream& in, Date& d);

13.2 Date.cpp

1#include"Date.h"
2
3void Date::Print() const
4{
5	cout << _year << "/" << _month << "/" << _day << endl;
6}
7
8bool Date::CheckDate() const
9{
10	if (_month < 1 || _month >12
11		|| _day < 1 || _day > GetMonthDay(_year, _month))
12	{
13		return false;
14	}
15	else
16	{
17		return true;
18	}
19}
20
21Date::Date(int year, int month, int day)
22{
23	_year = year;
24	_month = month;
25	_day = day;
26	if (!CheckDate())
27	{
28		cout << "非法日期:";
29		Print();
30	}
31}
32
33bool Date::operator<(const Date& d) const
34{
35	if (_year < d._year)
36	{
37		return true;
38	}
39	else if(_year == d._year)
40	{
41		if (_month < d._month)
42		{
43			return true;
44		}
45		else if(_month == d._month)
46		{
47			return _day < d._day;
48		}
49	}
50
51	return false;
52}
53
54bool Date::operator<=(const Date& d) const
55{
56	return *this < d || *this == d;
57}
58
59bool Date::operator>(const Date& d) const
60{
61	return !(*this <= d);
62}
63
64bool Date::operator>=(const Date& d) const
65{
66	return !(*this < d);
67}
68
69bool Date::operator!=(const Date& d) const
70{
71	return !(*this == d);
72}
73
74bool Date::operator==(const Date& d) const
75{
76	return _year == d._year
77		&& _month == d._month
78		&& _day == d._day;
79}
80
81Date& Date::operator+=(int day)
82{
83	if (day < 0)
84	{
85		return *this -= (-day);
86	}
87
88	_day += day;
89	while (_day > GetMonthDay(_year, _month))
90	{
91		_day -= GetMonthDay(_year, _month);
92		++_month;
93
94		if (_month == 13)
95		{
96			++_year;
97			_month = 1;
98		}
99	}
100
101	return *this;
102}
103
104Date Date::operator+(int day) const
105{
106	Date tmp = *this;
107	tmp += day;
108
109	return tmp;
110}
111
112Date& Date::operator-=(int day)
113{
114	if (day < 0)
115	{
116		return *this += (-day);
117	}
118	_day -= day;
119	while (_day <= 0)
120	{
121		_day += GetMonthDay(_year, _month);
122		--_month;
123
124		if (_month == 0)
125		{
126			--_year;
127			_month = 12;
128		}
129	}
130
131	return *this;
132}
133
134Date Date::operator-(int day) const
135{
136	Date tmp = *this;
137	tmp -= day;
138
139	return tmp;
140}
141
142//后置++
143Date Date::operator++(int)
144{
145	Date tmp = *this;
146	*this += 1;
147
148	return tmp;
149}
150
151//前置++
152Date& Date::operator++()
153{
154	*this += 1;
155	return *this;
156}
157
158//后置--
159Date Date::operator--(int)
160{
161	Date tmp = *this;
162	*this -= 1;
163
164	return tmp;
165}
166
167//前置--
168Date& Date::operator--()
169{
170	*this -= 1;
171	return *this;
172}
173
174int Date::operator-(const Date& d) const
175{
176	Date max = *this;
177	Date min = d;
178	//表示符号位
179	int flag = 1;
180
181	if (*this < d)
182	{
183		flag = -1;
184		max = d;
185		min = *this;
186	}
187
188	int n = 0;
189	while (min < max)
190	{
191		++min;
192		++n;
193	}
194
195	return n * flag;
196}
197
198ostream& operator<<(ostream& out, const Date& d)
199{
200	out << d._year << "/" << d._month << "/" << d._day << endl;
201	return out;
202}
203
204istream& operator>>(istream& in, Date& d)
205{
206	cout << "请依次输入年月日:>" << endl;
207	while (1)
208	{
209		in >> d._year >> d._month >> d._day;
210
211		if(!(d.CheckDate()))
212		{ 
213			cout << "输入日期非法:";
214			d.Print();
215			cout << "请重新输入:" << endl;
216		}
217		else
218		{
219			break;
220		}
221	}
222	return in;
223}

13.3 Test.cpp

1void TestDate1()
2{
3    Date d1(2025, 10, 28);
4	Date d2 = d1 + 100;
5	//Date d3(d1 + 100);
6	d1.Print();
7	d2.Print();
8
9    d1 += 100;
10	d1.Print();
11	
12	d1 -= 100;
13	d2 = d2 - 100;
14	d1.Print();
15	d2.Print();
16
17}
18
19void TestDate2()
20{
21	Date d1(2025, 10, 28);
22	d1++;
23	d1.Print();
24	++d1;
25	d1.Print();
26}
27
28void TestDate3()
29{
30	Date d1(2035, 10, 28);
31	Date d2(2025, 10, 28);
32
33	cout << d1 - d2 << endl;
34}
35
36void TestDate4()
37{
38	Date d1, d2;
39	cin >> d1 >> d2;
40	cout << d1 << d2;
41
42	cout << d1 - d2 << endl;
43}
44
45void TestDate5()
46{
47	const Date d1(2025, 10, 28);
48	d1.Print();
49
50	Date d2(2025, 10, 28);
51	d2.Print();
52
53	cout << &d1 << endl;
54	cout << &d2 << endl;
55}
56
57int main()
58{
59	//TestDate1();
60	//TestDate2();
61	//TestDate3();
62
63	//TestDate4();
64	TestDate5();
65	return 0;
66}

C++:类和对象---进阶篇》 是转载文章,点击查看原文


相关推荐


90%前端面试必问的12个JS核心,搞懂这些直接起飞!
良山有风来2025/11/5

你是不是也遇到过这样的场景?面试官抛出一个闭包问题,你支支吾吾答不上来;团队代码review时,看到同事用的Promise链一脸懵逼;明明功能实现了,性能却总是差那么一点... 别慌!今天我整理了12个JavaScript核心概念,这些都是2024年各大厂面试的高频考点,也是日常开发中真正实用的硬核知识。搞懂它们,不仅能轻松应对面试,更能让你的代码质量提升一个档次! 变量与作用域 先来看个最常见的面试题: // 经典面试题:猜猜输出什么? for (var i = 0; i < 3; i++)


OpenAI Aardvark:当AI化身代码守护者
墨风如雪2025/10/31

想象一下,一个不知疲倦、聪明绝顶的数字侦探,夜以继日地巡视你的代码,在每一个新提交、每一行变更中嗅探潜在的危险。这不是科幻,而是OpenAI在2025年末悄然放出的重磅炸弹——Aardvark。这款以“土豚”命名的AI智能体,并非简单的代码扫描器,它标志着AI在网络安全领域,真正迈出了“自主思考”的第一步。 认识你的新安全伙伴 Aardvark,由OpenAI最先进的GPT-5模型驱动,被定位为一个“agentic security researcher”。你可以把它理解为一位全职的“白帽黑客


CoAlbum:多级缓存与性能对比
RealmElysia2025/10/29

目录 Target 1.多级缓存生效注解 2.缓存上下文 3.责任链 Hander接口 责任链初始化 5.切面Aop Coalbum项目 使用缓存 性能对比 Jmeter tips Target Caffeine+Redis构建多级缓存,采用责任链模式,使用aop+注解的方式增强目标方法。若缓存命中,返回命中值。若没命中,执行目标方法,将目标方法返回值依次存入缓存链。 1.多级缓存生效注解 /** * 多级缓存生效注解 */ @Target


为超过10亿条记录的订单表新增字段
Sam_Deep_Thinking2025/10/26

如何为 10 亿行 MySQL 大表安全新增字段? 之前在一家餐饮公司待过几年。由于这家公司已经营业了很多年,MySQL 5.6 的订单表数据早已超过 10 亿行。这就带来一个经典难题: 如何为这张大表新增字段? 当时的处理方式 假设业务需求的上线时间是 2025 年 10 月 23 日,我们会安排组内同事在 10 月 22 日或 23 日凌晨 提交数据库变更: ALTER TABLE order_items ADD COLUMN new_flag TINYINT DEFAULT 0; 变


C 语言标准库头文件 time.h
hubenchang05152025/10/23

#C 语言标准库头文件 time.h 请查看 C 语言标准库头文件列表 了解更多相关 API。 这个头文件提供 线程 相关的功能。例如线程控制、互斥量、条件变量、线程局部存储等。 #常量 宏标准说明CLOCKS_PER_SECC89处理器的每秒时钟数,POSIX 上定义为 1000000 而非实际的时钟数TIME_UTCC11表示时间基准为 UTC 时间 #类型 类型标准说明time_tC89日历时间的时间戳类型(自 1970 年 1 月 1 日 00:00:00.000 UTC 以来的秒数


Redis(77)Redis缓存的优点和缺点是什么?
Victor3562025/10/22

Redis作为一个高性能的分布式缓存系统,具有许多优点,但也有一些缺点。以下详细讲解Redis缓存的优点和缺点,并结合代码说明如何尽量发挥其优点和规避其缺点。 优点 1. 高性能 Redis以其卓越的读写性能著称,能够支持高吞吐量的读写操作。 示例代码: import redis.clients.jedis.Jedis; public class RedisPerformanceExample { public static void main(String[] args) {


FFmpeg 基本数据结构 AVFormatConext 分析
给大佬递杯卡布奇诺2025/10/21

1、FFmpeg 主要数据结构分层设计 1.1 IO抽象层 协议层与 I/O 抽象层 (Protocol & I/O Abstraction),这一层负责从最广泛的数据源读取或写入数据。 核心数据结构:AVIOContext 功能: 抽象了底层的 I/O 操作。通过它,FFmpeg 可以用统一的接口处理文件、网络流(HTTP, RTMP, TCP)、内存缓冲区等。 关键点: 它使得上层的格式层(解复用)无需关心数据是从哪里来的。这对于播放网络直播流或处理内存中的媒体数据至关重


SpringCloud微服务项目实战——系统实现篇
thginWalker2025/10/20

06 服务多不易管理如何破——服务注册与发现 经过上一篇系统性的介绍 Spring Cloud 及 Spring Cloud Alibaba 项目,相信你已经对这两个项目有个整体直观的感受,本篇开始正式进入本课程的第二部分,一起进入业务的开发阶段。 服务调用问题 在分析业务需求时,其中有个简单的功能点:会员可以开通月卡,开通月卡的同时,需要增加相应的积分。开通月卡功能在会员服务模块维护,但增加积分功能在积分服务模块维护,这就涉及到两个模块间的服务调用问题。 单实例情况:可以采用点对点的


linux系统jdk&&mysql配置
阑梦清川2025/10/18

使用ubuntu进行举例说明: 更新软件包 sudo apt update 安装JDK sudo apt install openjdk-17-jdk 关于mysql, #查找安装包 apt list |grep "mysql-server" #安装mysql sudo apt install mysql-server 查看数据库的状态: sudo systemctl status mysql 如果遇到问题,参考下面的这个: 这个主要是刷新权限表,然后修改我们的密码,否则会报错


【机器学习入门】8.1 降维的概念和意义:一文读懂降维的概念与意义 —— 从 “维度灾难” 到低维嵌入
做科研的周师兄2025/10/17

对于刚入门机器学习的同学来说,“高维数据” 是很容易遇到的痛点 —— 比如处理包含几十甚至上百个特征的数据集时,不仅训练速度变慢,模型还可能因为 “维度太多” 出现泛化能力下降的问题。而 “降维” 正是解决高维数据困境的核心技术。今天我们就从基础概念出发,拆解 “维度灾难” 的危害、降维的本质,以及经典的低维嵌入方法,帮你彻底理解降维为什么重要、到底在做什么。 一、先搞懂:什么是 “维度”?为什么会有 “维度灾难”? 在学习降维前,我们需要先明确 “维度” 的定义,以及高维数据会带来的核心问

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0