C++知识点总结

一、

问题:现在我们尝试去重载operator<<,然后发现我们没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以我们要将operator<<重载成全局函数。但是这样的话,又会导致类外没办法访问成员,那么这里就需要友元来解决。operator>>同理。

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	ostream& operator<<(ostream& _cout)
	{
		_cout << _year << "-" << _month << "-" << _day;
		return _cout;
	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	
	Date d(2017, 12, 24);
	d << cout;  //2017-12-24
	return 0;
}

二、

友元函数:

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

#include<iostream>
using namespace std;
class Date
{
	friend ostream& operator<<(ostream& _cout, const Date& d);
	friend istream& operator>>(istream& _cin, Date& d);
public:
	Date()
	{

	}
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}
istream& operator>>(istream& _cin, Date& d) //不能是const Date& d 否则会报错
{
	_cin >> d._year;
	_cin >> d._month;
	_cin >> d._day;
	return _cin;
}

int main()
{
	Date d;
	cin >> d; //输入2022 9 24
	cout << d << endl; //输出2022-9-24
	return 0;
}

结果如下:

  •  友元函数不能用const修饰
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用和原理相同

友元类:

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元关系是单向的,不具有交换性。
比如Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time
类的私有成员变量
,但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递:如果B是A的友元,C是B的友元,则不能说明C时A的友元。
 

#include<iostream>
using namespace std;
class Date; // 前置声明
class Time
{
	friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:
	Time()  //必须有
	{

	}
	Time(int hour, int minute, int second)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}
private:
	int _hour=0;
	int _minute=0;
	int _second=0;
};
class Date
{
public:
	Date()  //可以没有
	{

	}

	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	void SetTimeOfDate(int hour, int minute, int second)
	{
		// 直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}
	void print(Date d)
	{
		cout << d._year << " " << d._month << " " << d._day << " " << d._t._hour << ":" << d._t._minute << ":" << d._t._second << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

int main()
{
	Date d(2022, 10, 18);
	d.SetTimeOfDate(13, 40, 00);
	d.print(d);
	return 0;
}

 

三、内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。注意此时这个内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类。注意友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系。

#include<iostream>
using namespace std;
class A
{
private:
	static int k;
	int h;
public:
	class B
	{
	public:
		void foo(const A& a)
		{
			
			cout << k << endl;//OK
			cout << a.h << endl;//OK
		}
	};
};
int A::k = 1;
int main()
{
	A::B b;
	b.foo(A());  //输出1 换行 0
	return 0;
}

四、

#include<stdio.h>
// 以C语言的方式重新实现上述功能
// 在C语言中,所看即所得
struct Date
{
	int year;
	int month;
	int day;
};

void Init(Date* pthis, int year, int month, int day)
{
	pthis->year = year;
	pthis->month = month;
	pthis->day = day;
}

void Print(Date* pthis)
{
	printf("%d-%d-%d\n", pthis->year, pthis->month, pthis->day);
}

int main()
{
	Date d1;
	Init(&d1, 2022, 4, 7);
	Print(&d1); //2022-4-7

	Date d2;
	Init(&d2, 2022, 4, 8);
	Print(&d2); //2022-4-8
	return 0;
}

五、如果成员函数是通过指针调用,this有可能会为nullptr

class Date
{
public:
	void Init(int year, int month, int day)
	{
		// cout << &this << endl;
		int a = 10;
		int& ra = a;


		Date* const & rthis = this;   // rthis就是this的别名
		cout << &rthis << endl;

		_year = year;
		_month = month;
		_day = day;
	}


	void TestThis()
	{
		cout << this << endl;
		cout << "Date::TestThis()" << endl;
	}

	void Print()
	{
		cout << this << endl;
		cout << this->_year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Init(2022, 4, 7);
	d1.Print();//2022-4-7

	Date* pd = &d1;
	pd->Print();//2022-4-7

	// 如果成员函数是通过指针调用,this有可能会为nullptr
	// 当指针指向nullptr的时候,this就是nullptr 以下代码会引发异常
	pd = nullptr;
	pd->TestThis();    // Date::TestThis(pd);
	pd->Print();
	return 0;
}

 注释掉部分代码后:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		// cout << &this << endl;
		int a = 10;
		int& ra = a;


		Date* const & rthis = this;   // rthis就是this的别名
		cout << &rthis << endl;

		_year = year;
		_month = month;
		_day = day;
	}


	void TestThis()
	{
		cout << this << endl;
		cout << "Date::TestThis()" << endl;
	}

	void Print()
	{
		cout << this << endl;
		cout << this->_year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Init(2022, 4, 7);
	d1.Print();

	Date* pd = &d1;
	pd->Print();

	// 如果成员函数是通过指针调用,this有可能会为nullptr
	// 当指针指向nullptr的时候,this就是nullptr
	
	//pd = nullptr;
	//pd->TestThis();    // Date::TestThis(pd);
	//pd->Print();
	return 0;
}

 六、无参构造和全缺省构造函数。统称为默认构造函数,且不能共存


class Time
{
public:
	Time()
	{
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	// 无参构造和全缺省构造函数,统称为默认的构造函数
	// 并且不能共存的
	Date()
	{}

	Date(int year = 1900, 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;
	Time _t;
};
int main()
{
	Date d1(2022, 4, 7);
	Date d; // 报错:编译器发现调用无参构造函数可以,调用全缺省的构造函数也可以
	return 0;
}

七、如果用户没有显式提供任何构造函数,则编译器会生成一个无参的默认构造函数

class Date
{
public:
	/*
	如果用户没有显式提供任何构造函数,则编译器会生成一个无参的默认构造函数
	Date()
	{}

	当Date d;  发现d对象创建好之后,d对象的成员变量全部都是随机值
	也就是说编译器生成的无参构造函数没有意义
	是不是说:编译器生成的无参构造函数一定是没有意义的呢?  答案:no
	*/

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d;
	d.Print();//-858993460--858993460--858993460
	return 0;
}

八、

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};


class Date
{
public:
	/*
	如果用户没有显式提供任何构造函数,则编译器会生成一个无参的默认构造函数
	Date()
	{}

	当Date d;  发现d对象创建好之后,d对象的成员变量全部都是随机值
	也就是说编译器生成的无参构造函数没有意义
	是不是说:编译器生成的无参构造函数一定是没有意义的呢?  答案:no
	*/

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	// C++11中:为了解决默认构造函数不对内置类型初始化问题
	int _year=1900;
	int _month = 1;
	int _day = 1;
	Time _t;   // 日期类型对象中包含了一个时间对象
};

int main()
{
	Date d;   // 创建那个类的对象,编译器就会调用该类的构造函数
	return 0;
}

 九、拷贝构造函数

编译器会生成默认的构造函数,但一旦类中涉及到资源的管理,拷贝构造函数一定要显示实现。

// 是否就说明:直接使用编译器生成的默认拷贝构造函数就可以了呢?
// 答案:对于日期类这种没有涉及到资源管理的类是可以的
// 一旦类中涉及到资源的管理,拷贝构造函数一定要实现
class Date
{
public:
	Date(int year = 1900, 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(2022, 4, 12);
	Date d2(d1);
	d2.Print();
	_CrtDumpMemoryLeaks();
	return 0;
}

 以下程序会触发异常:

#include <stdlib.h>
#include <assert.h>

typedef int DataType;

// Stack没有显式实现拷贝构造函数,则编译器会生成一份默认的拷贝构造函数
class Stack
{
public:
	Stack()
	{
		_array = (DataType*)malloc(sizeof(DataType)* 10);
		if (NULL == _array)
		{
			assert(0);
			return;
		}

		_capacity = 10;
		_size = 0;
	}

	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}

	void Push(const DataType& data)
	{
		// CheckCapcity();
		_array[_size] = data;
		_size++;
	}

	void Pop()
	{
		if (Empty())
			return;

		--_size;
	}

	DataType Top()
	{
		return _array[_size - 1];
	}

	size_t Size()
	{
		return _size;
	}

	bool Empty()
	{
		return 0 == _size;
	}
private:
	DataType* _array;
	size_t _capacity;
	size_t _size;
};


void TestStack()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);

	Stack s2(s1); //涉及到资源管理,需要显示实现拷贝构造函数
	cout << s2.Size() << endl;
	cout << s2.Top() << endl;
}


int main()
{
	TestStack();
	return 0;
}

对于自定义类型作为函数的参数或者返回值时,能传递引用尽量传递引用,否则会调用拷贝构造函数,让程序效率低下。

// 拷贝构造函数调用场景
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 拷贝构造函数
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		cout << "Date(const Date&)" << endl;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	} 
private:
	int _year;
	int _month;
	int _day;
};


// 2. 以类类型方式传参--以值的方式
void TestDate1(Date d)
{}


// 3. 以类类型方式作为函数的返回值---以值的方式返回的
Date TestDate2()
{
	Date d(2022, 4, 13);
	return d;
}


Date TestDate3()
{
	// 创建一个匿名对象:即没有名字的对象
	// 在返回时,编译器不会在通过拷贝构造函数创建一个临时对象返回
    // 而是将匿名对象直接返回了
	// 相当于以匿名对象直接返回时,编译器会做优化
	return Date(2022, 4, 13);
}


// 告诉一个结论:对于自定义类型作为函数的参数或者返回值时,能传递引用尽量传递引用
Date& TestDate4(Date& d)
{
	return d;
}

int main()
{
	// 1. 用一个对象直接构造一个新对象
	Date d1(2022, 4, 12);
	Date d2(d1);//输出Date(const Date&)
	d2.Print();//输出2022-4-12

	TestDate1(d1); //自定义类型作为函数参数,这里调用了拷贝构造函数 输出Date(const Date&)
	d2 = TestDate2(); //自定义类型作为函数返回值,也调用了拷贝构造函数,生成一份临时对象,然后返回。 输出Date(const Date&)
	d2 = TestDate3(); //相当于以匿名对象直接返回,不会调用拷贝构造函数,效率更高。
	d2 = TestDate4(d1);//以引用类型作为参数,不会调用拷贝构造函数。
	return 0;
}

 十、构造函数

// 构造
class Date
{
public:
	/*
	// 如果编译器会给Date类生成的构造函数,对于当前的Date类,生成的构造函数一定是下面:
	Date()
	{}
	上述构造函数没有任何意义,掉完之后对象中日期还是随机值
	那生成和不生成没有任何影响,反而生成之后程序运行的效率就降低了---干脆:对于这种没有意义的构造函数就不用生成
	*/

	void PrintDate()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

当A类中包含有B类的对象时,如果A类没有显式定义任何构造函数,而B类显式提供了默认的构造函数。则:编译器一定会给A类生成默认的构造函数,目的就是要将A类中包含的B类的对象调用B类的构造函数初始化完整 。A-Date  B-Time

class Time
{
public:
	Time(int hour = 0, int minute = 0, int second = 0)
	{
		_hour = hour;
		_minute = minute;
		_second = second;
	}

private:
	int _hour;
	int _minute;
	int _second;
};

/*
当A类中包含有B类的对象时,如果A类没有显式定义任何构造函数,而B类显式提供了默认的构造函数
则:编译器一定会给A类生成默认的构造函数,目的就是要将A类中包含的B类的对象调用B类的构造函数初始化完整
Date--->A
Time--->B
*/
class Date
{
public:
	void PrintDate()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;  // 类类型对象,并且Time具有默认的构造函数
};

void TestDate()
{
	Date d;
	d.PrintDate();
}

int main()
{
	TestDate();
	return 0;
}

十一、赋值运算符重载

// 赋值运算符重载
// 对于Date类编译器不会生成与赋值有关的函数
class Date
{
public:
	Date(int year=1900, int month=1, int day=1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void PrintDate()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

void TestDate()
{
	Date d1(2022, 4, 16);
	d1.PrintDate();//2022-4-16
	
	Date d2;
	d2.PrintDate();//1900-1-1
	
	Date d3 = d1;    // 等价于:Date d3(d1)  调用拷贝构造函数
	d3.PrintDate(); //2022-4-16
	
	d2 = d1;        // 赋值
	d2.PrintDate(); //2022-4-16
}


int main()
{
	TestDate();
	_CrtDumpMemoryLeaks();
	return 0;
}
// 运算符重载:
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}


	bool IsEqual(const Date& d)
	{
		return _year == d._year &&
			   _month == d._month &&
			   _day == d._day;
	}

	void PrintDate()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
//private:

	int _year;
	int _month;
	int _day;
};

/*
1. 什么是运算符重载
2. 为什么要重载
3. 如何重载
4. 和函数重载区别?
*/
// 运算符重载目的:告诉编译器,自定义的对象遇到该运算符时应该如何操作
// 返回值类型 operator操作符(参数列表)
bool operator==(const Date& left, const Date& right)
{
	return left._year == right._year &&
		   left._month == right._month &&
		   left._day == right._day;
}

void TestDate()
{
	// 内置类型定义的变量,可以直接进行相关运算符的操作
	int a = 10;
	int b = 20;
	if (a < b)
	{
		cout << "a < b" << endl;
	}
	else if (a == b)
	{
		cout << "a == b" << endl;
	}
	else
	{
		cout << "a > b" << endl;
	}

	Date d1(2022, 4, 16);
	Date d2(2022, 4, 17);

	// 对于自定义类型的对象,不能直接使用对应的运算符,比如:要判断两个对象是否相等
	// 原因:自定义类型的对象中包含有多个成员,如何进行比较编译器不知道
	// 编译报错
	// 可读性更高,但是编译器不能直接支持
	//if (d1 == d2)
	//{
	//	;
	//}

	if (d1.IsEqual(d2))
	{
		cout << "d1 == d2" << endl;
	}
	else
	{
		cout << "d1 != d2" << endl;
	}

	operator==(d1, d2);
	if (d1 == d2)   // 此处调用:operator==(d1,d2);
	{
		cout << "d1 == d2" << endl;
	}
	else
	{
		cout << "d1 != d2" << endl;
	}
}

int main()
{
	TestDate();
	return 0;
}

 十二、复数类

// 复数类
class Complex
{
public:
	Complex(double real = 0.0, double image = 0.0)
	{
		_real = real;
		_image = image;
	}


	/*
	// error C2804: 二进制“operator *”的参数太多
	// 成员函数有一个隐藏的this指针
	Complex operator*(const Complex& left, const Complex& right)
	{
		return left;
	}
	*/

	// 复数类在相乘时,人家有自己特定的规则,不是实部乘实部,虚部乘虚部
	Complex operator*(const Complex& c)
	{
		return Complex();
	}

	double _real;
	double _image;
};

Complex operator+(const Complex& left, const Complex& right)
{
	//Complex temp(left._real+right._real, left._image + right._image);
	//return temp;
	return Complex(left._real + right._real, left._image + right._image);
}

// 3.每个运算符要复合其含义
Complex operator-(const Complex& left, const Complex& right)
{
	//Complex temp(left._real+right._real, left._image + right._image);
	//return temp;
	return Complex(left._real + right._real, left._image + right._image);
}

int main()
{
	Complex c1(1.0, 2.0);
	Complex c2(3.0, 4.0);
	Complex c;
	c = c1 + c2;
	//c = c2 - c1;
	return 0;
}

 十三、前置++、后置++重载

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	// 运算符重载时一定要复合其含义
	// d1 = d2 = d3;
	// d1.operator=(d2.operator=(d3));

	// 
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		
		return *this;
	}

	void PrintDate()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}


	// 比如:重载++
	// ++是单目运算符--只需要一个操作数
	// ret = ++d;结果也是日期

	// 前置++
	Date& operator++()
	{
		_day += 1;
		return *this;
	}

	// 后置++
	// ret = d++;
	// 注意:为了让前置++和后置++同时存在,语法说:在重载后置++的时候,可以多增加一个int参数
	//                                      目的让前置++和后置++形成重载
	// 后置++需要返回+1之前的结果
	Date operator++(int)
	{
		Date temp(*this);
		_day += 1;
		return temp;
	}

	Date& operator--()
	{
		_day -= 1;
		return *this;
	}

	Date operator--(int)
	{
		Date temp(*this);
		_day -= 1;
		return temp;
	}
private:
	int _year;
	int _month;
	int _day;
};

// 注意:赋值运算符重载必须要重载成类的成员函数,否则编译失败
// 因为:赋值运算符重载是默认成员函数,如果用户没有显式实现时,则编译器会自动生成一份
// 如果自己再在类外实现一份  相当于赋值运算符重载存在两份
//void operator=(const Date& left, const Date& right)
//{}

void TestDate()
{
	Date d1(2022, 4, 16);
	Date d2(2022, 4, 17);
	Date ret;
	ret = ++d1;
	ret.PrintDate(); //2022-4-17
	
	ret = d1++;
	ret.PrintDate(); //2022-4-17
}

int main()
{
	TestDate();
	Date d1(2022, 4, 16);
	Date d2(2022, 4, 17);

	d1 = d2;
	d1.PrintDate();//2022-4-17

	int a = 1;
	int b = 2;
	int c = 3;
	a = b;
	a = b = c;   // =可以支持连续赋值
	cout <<"a b c的值:" <<a << " " << b << " " << c << endl;

	Date d3(2022,4,18);
	d1 = d2;
	d1.PrintDate(); //2022-4-17

	d1.operator=(d2);
	d1.PrintDate();  //2022-4-17

	// d1 = d2 = d3;   // 本质是:两次operator=函数的调用
	// 先用d3给d2赋值  再用d2给d3赋值
	// d1.operator=(d2.operator=(d3));

	d1 = d1;

	Date& d4 = d1;

	//....

	//...
	
	d4 = d1;
	return 0;
}

 

 十四、对&进行重载(取地址同时将地址打印出来)

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date* operator&()
	{
		cout << this << endl;
		_day += 1;
		return this;
	}

	
	const Date* operator&()const //第一个const限制的是返回值  第二个是指成员变量不能被修改 用const修饰成员函数,称其为const成员函数,在该函数内部不能修改任何成员变量。
	{
		cout << this << endl;
		return this;
	}
	
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(2022, 4, 16);

	// 需求:在对Date类对象取地址,顺便将该对象的地址打印出来
	Date* pd = &d1;//在取地址瞬间也对变量做了修改,不安全

	const Date d2(d1); //如果是const型对象,就不会对变量做出修改。编译器不会让const对象调用普通成员函数。
	const Date* pd2 = &d2;//因为d2是const型,所以pd2也必须是const类型  让const类型对象取地址瞬间也打印地址 
	return 0;
}

十五、const修饰成员函数

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 该函数只需将对象中的成员变量打印出来就可以了
	// 在运行的整个阶段不需要对成员变量进行修改
	// const修饰该成员函数即可---const成员函数
	void PrintDate()const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
		// _day += 1;
	}

	void SetYear(int year)
	{
		cout << typeid(this).name() << endl;
		_year = year;
	}

	int GetYear()const
	{
		cout << typeid(this).name() << endl;
		//_year += 1;
		return _year;
	}

	// this的类型:Date* const
	Date* operator&()
	{
		return this;
	}

	// this的类型:const Date* const
	const Date* operator&()const
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d(2022, 4, 16);
	d.PrintDate(); //2022-4-16

	d.SetYear(2023);//class Date * __ptr64
	cout << d.GetYear() << endl; //class Date const * __ptr64  2023
	return 0;
}

 

 十六、const成员函数和普通成员函数

  • const类型对象,只能调用const成员函数,不能调用普通成员函数
  • 普通对象,普通成员函数和const成员函数都可以调用
  • 在普通成员函数中,既可以调用普通成员函数也可以调用const成员函数
  • const成员函数本质就是在修饰this,表明this指向对象中成员是不能被修改的,所以const成员函数中不能调用普通成员函数
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	// 在该成员函数中对当前对象:可读可写
	void func1()
	{
		_day += 1;
	}

	// 在该成员函数中对当前对象:只读
	void func2()const
	{
		// _day += 1;   // 编译报错
		cout << _day << endl;
	}


	// 结论:普通成员函数中--既可以调用普通成员函数,也可以调用const成员函数
	void func3()
	{
		_day += 1;
		this->func1();
		this->func2();
	}

	// 能否调用普通成员函数
	void func4()const
	{
		// _day += 1;   // 编译报错

		/*const 修饰成员函数  本质就是在修饰this,表明this指向对象中成员不能修改的
		如果编译器允许在const成员函数中调用func1的普通成员函数,那么func1执行完成之后
		很可能会将this指向对象中成员修改掉---代码不安全,结论:
		const成员函数中不能调用普通成员函数
		*/
		// this->func1();
		this->func2();   // 本来就是const成员函数,即func2中一定不会对this指向的对象修改
	}


	// 需求:除了一个成员变量可以允许被修改,其余的都不能被修改
	void func5()const
	{
		// _year = 1900;
		// _month = 1;
		_day = 1;
	}
private:
	int _year;
	int _month;
	mutable int _day;   // mutable修饰成员变量意思:该成员变量可以在const中被修改
};
int main()
{
	return 0;
}

十七、const

// 构造函数体,{}的内部,并不是对成员变量进行初化,而是对成员变量进行赋值
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;

		// 赋值可以赋值多次,但是初始化只能初始化一次
		_day = day;
		_day = day;
		_day = day;
		_day = day;
		// _a = 10;
	}

private:
	int _year;
	int _month;
	int _day;
	const int _a=10;  //必须给个初值 
};

int main()
{
	// const修饰的是一个常量,而且在编译阶段会进行替换
	// const修饰的常量在定义时必须要初始化
	const int a =1;
	return 0;
}

十八、初始化列表

// 那些成员必须在初始化列表的位置进行初始化?
class Time
{
public:
	Time(int hour, int minute, int second)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{
		cout << "Time(int,int,int)" << endl;
	}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	// 每个成员变量在初始化列表中只能出现一次
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
		, _a(10)
		, _rb(_day)
		, _t(16,15,30)
	{
		cout << "Date(int,int,int)" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
	const int _a;    // const类型的成员变量
	int& _rb;        // 引用类型的成员变量
	Time _t;         // 类类型对象,并且该类的构造函数不是默认的构造函数时候
};

十九、初始化列表  Date Time

Time必须有默认构造函数(全缺省参数也算),Date的初始化列表才不用对Time类对象进行初始化。

class Time
{
public:
	Time(int hour = 0, int minute = 0, int second = 0)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{
		cout << "Time(int,int,int)" << endl;
	}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
public:
	// 每个成员变量在初始化列表中只能出现一次
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{
		cout << "Date(int,int,int)" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
	Time _t;         // 类类型对象,并且该类的构造函数不是默认的构造函数时候
};
int main()
{
	const int a = 10;
	int b = 10;
	int& rb = b;

	Date d(2022, 4, 16);
	return 0;
}

 二十、初始化列表 

初始化列表中:真正的初始化次序与成员变量在类中声明的次序一致,与成员变量在初始化列表中
出现的先后次序无关

class Date
{
public:
	// Date d(2022, 4, 10);
	// 初始化列表中:真正的初始化次序与成员变量在类中声明的次序一致,与成员变量在初始化列表中
	// 出现的先后次序无关
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _day(day)
		, _month(_day)
	{
		cout << "Date(int,int,int)" << endl;
	}

	Date(const Date& d)
		: _year(d._year)
		, _month(d._month)
		, _day(d._day)
	{}

private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d(2022, 4, 10);
	return 0;
}

 二十一、explicit

在构造函数前加上explicit会抑制数字向对象的隐式转换。代码就不会通过编译。


// 单参构造函数具有类型转换的作用
class Date
{
public:
	// 单参构造函数
	explicit Date(int year, int month = 1)
		: _year(year)
		, _month(month)
		, _day(1)
	{
		cout << "Date(int,int,int):"<<this << endl;
	}

	Date& operator=(const Date& d)
	{
		cout << this << "=" << &d << endl;
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}

	~Date()
	{
		cout << this << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	int a = 10;
	int b = 20;
	a = b;   // 逻辑才是正确的,相同类型的变量相互赋值

	double d = 12.34;
	a = d;   // 类型不同,但是int和double之间本来就可以进行隐式类型转换
	cout << a << endl;
	Date dd(2023);
	/*
	1. 编译器先使用2022调用单参构造函数创建一个临时对象
	2. 使用临时对象给dd进行赋值
	3. 赋值完之后,将临时对象销毁掉
	*/
	dd = 2022;   // d是日期类型的对象,2022是一个整形数字,天哪整形数字竟然还可以给日期对象赋值?
	return 0;
}

去掉explicit之后: 

// 单参构造函数具有类型转换的作用
class Date
{
public:
	// 单参构造函数
	 Date(int year, int month = 1)
		: _year(year)
		, _month(month)
		, _day(1)
	{
		cout << "Date(int,int,int):"<<this << endl;
	}

	Date& operator=(const Date& d)
	{
		cout << this << "=" << &d << endl;
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}

	~Date()
	{
		cout << this << endl;
	}
	void printf()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	int a = 10;
	int b = 20;
	a = b;   // 逻辑才是正确的,相同类型的变量相互赋值

	double d = 12.34;
	a = d;   // 类型不同,但是int和double之间本来就可以进行隐式类型转换
	cout << a << endl;
	Date dd(2023);
	/*
	1. 编译器先使用2022调用单参构造函数创建一个临时对象
	2. 使用临时对象给dd进行赋值
	3. 赋值完之后,将临时对象销毁掉
	*/
	dd = 2022;   // d是日期类型的对象,2022是一个整形数字,天哪整形数字竟然还可以给日期对象赋值?
	dd.printf();
	return 0;
}

 二十二、static

静态成员变量在类中只是声明,需要在类外重新进行定义,在类外定义时不加static,但要加上类名。eg:int Student::a=0;

class Sum
{
public:
	Sum()
	{
		++count;
		sum += count;
	}

	static int GetSum()
	{
		return sum;
	}

	static void Reset()
	{
		count = 0;
		sum = 0;
	}
private:
	static int count;
	static int sum;
};

int Sum::count = 0;
int Sum::sum = 0;



class Solution {
public:
	int Sum_Solution(int n) {
		Sum::Reset();
		// 在堆上创建了n个对象
		Sum* pt = new Sum[n];
		delete[] pt;
		return Sum::GetSum();
	}
};

int main()
{
	Solution s;
	cout << s.Sum_Solution(5) << endl; // Sum::count=5   Sum::sum=15
	cout << s.Sum_Solution(5) << endl; // 15 注意函数里调用了Reset,即原先的1+2+3+4+5已经被清零。如果不调用Reset,该函数输出55(15+6+7+8+9+10)
	return 0;
}

二十三、<<运算符重载

声明其为友元函数,在类外可以访问该类的私有成员变量。

class Date
{
private:
	friend ostream& operator<<(ostream& _cout, const Date& d);
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{
		// 注意:构造日期是否合法
		if (!(_year > 0 &&
			(_month >= 1 && _month <= 12) &&
			(_day >= 1 && _day <= GetDaysOfMonth(_year, _month))))
		{
			_year = 1900;
			_month = 1;
			_day = 1;
		}
	}

	Date(const Date& d)
		: _year(d._year)
		, _month(d._month)
		, _day(d._day)
	{}

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}

		return *this;
	}

	~Date()
	{}

	// 两个日期对象可以相减---求两个日期之间相差了多少天
	// a - b
	// Date d1(2022,4,19);
	// Date d2(2022,5,1);
	// d1 - d2 = 5    d2 - d1 = 5
	int operator-(const Date& d)const
	{
		Date minDate(*this);
		Date maxDate(d);
		if (minDate > maxDate)
		{
			minDate = d;
			maxDate = *this;
		}

		int count = 0;
		while (minDate != maxDate)
		{
			++minDate;
			++count;
		}

		return count;
	}

	bool operator==(const Date& d)const
	{
		return _year == d._year &&
			_month == d._month &&
			_day == d._day;
	}

	bool operator!=(const Date& d)const
	{
		return !(*this == d);
	}

	bool operator>(const Date& d)const
	{
		if ((_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day))
		{
			return true;
		}

		return false;
	}

	bool operator<(const Date& d)const
	{
		return !(*this == d || *this > d);
	}

	// 100天之后是哪一天?  d1+100
	// Date d1(2022,4,19);
	// d1+5===>2022,4,24
	// d1+100===>?
	// a+b  a+=b
	Date operator+(int days)
	{
		if (days < 0)
		{
			return *this - (0 - days);
		}

		Date temp(*this);
		temp._day += days;

		int daysOfMonth = 0;
		// 检测temp中的天数是否合法
		while (temp._day >(daysOfMonth = GetDaysOfMonth(temp._year, temp._month)))
		{
			temp._day -= daysOfMonth;
			temp._month++;

			if (13 == temp._month)
			{
				temp._year += 1;
				temp._month = 1;
			}
		}
		return temp;
	}

	// 100天以前是哪一天?  d1-100
	Date operator-(int days)
	{
		if (days < 0)
		{
			return *this + (0 - days);
		}

		Date temp(*this);
		temp._day -= days;


		while (temp._day <= 0)
		{
			temp._month -= 1;
			if (0 == temp._month)
			{
				temp._year -= 1;
				temp._month = 12;
			}

			temp._day += GetDaysOfMonth(temp._year, temp._month);
		}
		return temp;
	}

	// 电子日历日期会自动加
	// 前置++
	Date& operator++()
	{
		*this = *this + 1;
		return *this;
	}

	Date operator++(int)
	{
		Date temp(*this);
		*this = *this + 1;
		return temp;
	}

	// 倒计时
	Date& operator--()
	{
		*this = *this - 1;
		return *this;
	}

	Date operator--(int)
	{
		Date temp(*this);
		*this = *this - 1;
		return temp;
	}

	bool IsLeapYear()const
	{
		return IsLeapYear(_year);
	}

	void PrintDate()const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

#if 0
	// 可以对流插入运算符进行重载
	// <<有两个操作数:1. 输出流对象   2. 要打印的内容
	void operator<<(/*Date* cont this,*/ostream& _cout)
	{
		_cout << _year << "-" << _month << "-" << _day;
	}

	// d << cout;   cout << d
#endif


private:
	int GetDaysOfMonth(int year, int month)const
	{
		int days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
		if (2 == month && IsLeapYear(year))
		{
			days[2] += 1;
		}

		return days[month];
	}

	bool IsLeapYear(int year)const
	{
		if ((0 == year % 4 && 0 != year % 100) ||
			(0 == year % 400))
		{
			return true;
		}

		return false;
	}

	int GetYear()const
	{
		return _year;
	}

	int GetMonth()const
	{
		return _month;
	}

	int GetDay()const
	{
		return _day;
	}

//public:
private:
	int _year;
	int _month;
	int _day;
};


// 在<<重载的时候,最好不要加格式控制,比如换行
ostream& operator<<(ostream& _cout, const Date& d)
{
	//_cout << d.GetYear() << "-" << d.GetMonth() << "-" << d.GetDay();
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

int main()
{
	Date d(2022, 4, 19);
	Date d1(2022, 4, 19);
	d.PrintDate();

	// cout << d;  // 编译失败
	//d << cout;
	//d.operator<<(cout);
	// cout << d << endl;

	cout << 123 << " " << 12.34 << endl;
	cout << d;
	operator<<(cout, d);
	cout << d << d1;

	operator<<(operator<<(cout, d), d1);
	// cout << d << endl;
	cout << "hello world!!!";
	cout << "hello bit" << endl;
	return 0;
}

 二十四、friend 

友元函数位置不影响,即不受访问权限服的限制

 另外,感觉是标准写法

class A
{
	friend void TestFriend();
public:
	int GetA()const
	{
		return _a;
	}
private:
	int _a;
};


class B
{
public:
	int GetB()const
	{
		return _b;
	}
private:
	int _b;

	friend void TestFriend();
};


void TestFriend()
{
	A aa;
	aa._a = 10;

	B bb;
	bb._b = 20;

	cout << aa.GetA() << endl; //10
	cout << bb.GetB() << endl; //20
}
int main()
{
	TestFriend();
}

 二十五、Date,Time类

class Time
{
	friend class Date;
public:
	Time(int hour, int minute, int second)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}

private:
	void f()const
	{
		//Date d(2022,4,23);
	}

private:
	int _hour;
	int _minute;
	int _second;
};


class Date
{
public:
	Date(int year, int month, int day)
		: _year(year)
		, _month(month)
		, _day(day)
		, _t(0,0,0)
	{}

	void PrintDate()const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
		cout << _t._hour << ":" << _t._minute << ":" << _t._second << endl;
		_t.f();
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
int main()
{
	Date d(2022, 10, 25);
	d.PrintDate();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值