Bootstrap

c++学习第七天

创作过程中难免有不足,若您发现本文内容有误,恳请不吝赐教。


提示:以下是本篇文章正文内容,下面案例可供参考。

一、const成员函数

//Date.h

#pragma once

#include<iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1);
    
    //两个Print()可以同时存在,构成函数重载
	void Print() const;
	void Print();

private:
	int _year;
	int _month;
	int _day;
};
//Date.cpp

#include"Date.h"

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	// 检查日期是否合法
	if (month < 1 || month > 12
		|| day < 1 || day > GetMonthDay(year, month))
	{
		cout << "非法日期" << endl;
		// exit(-1);
	}
}

//void Date::Print(const Date* this) 
void Date::Print() const
{
	cout << _year << "年" << _month << "月" << _day << "日" << endl;
}

//void Date::Print(Date* this) 
void Date::Print()
{
	cout << _year << "年" << _month << "月"  << endl;
}
//Test.cpp

#include"Date.h"

int main()
{
	const Date d1(2025, 1, 1);
	d1.Print();

	Date d2(2025, 1, 2);
	d2.Print();
}


//Date.h

    bool operator<(const Date& d) const;
	bool operator==(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator>(const Date& d) const;
	bool operator>=(const Date& d) const;
	bool operator!=(const Date& d) const;

	Date& operator+=(int day);
	Date operator+(int day) const;
	Date& operator-=(int day);
	Date operator-(int day) const;

	
	Date& operator++();
	Date operator++(int);
	Date& operator--();
	Date operator--(int);

	int operator-(const Date& d) const;

        简单来说:只读函数可以加const,内部不涉及修改成员的都是只读函数


二、取地址及const取地址操作符重载

//Test.cpp

#include"Date.h"

int main()
{
	const Date d1(2025, 1, 2);
	d1.Print();
	cout << &d1 << endl;

	Date d2(2025, 1, 7);
	d2.Print();
	cout << &d2 << endl;

	return 0;
}

        理论上上面的代码是不能运行的,因为自定义类型使用运算符需要重载。因为取地址及const取地址操作符重载这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

//自己写如下:
//日常使用自动生成的就可以了

Date* operator&()
{
	return this;
}

const Date* operator&() const
{
	return this;
}
// 不想被取到有效地址才自己写
Date* operator&()
{
	return nullptr;
}

const Date* operator&() const
{
	return this;
}


三、流插入和流提取

//Date.h

void operator<<(ostream& out);
//Date.cpp

void Date::operator<<(ostream& out)
{
	out << _year << "/" << _month << "/" << _day << endl;
}
//Test.cpp

#include"Date.h"

int main()
{
	Date d1(2025, 1, 21);

	cout << d1 ;

	return 0;
}

原因:参数顺序不匹配

//Test.cpp

#include"Date.h"

int main()
{
	Date d1(2025, 1, 21);

	d1 << cout ;

	return 0;
}


 不能写成成员函数,因为成员函数this指针永远占据第一个,所以只能往全局上写。

//Date.h

#pragma once

#include<iostream>
using namespace std;

class Date
{
	// 友元声明
    //因为operator<<写在函数外面不能访问到_year,_month,_day
	friend void operator<<(ostream& out, const Date& d);

    Date(int year = 1, int month = 1, int day = 1);

public:

	void Print();

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

void operator<<(ostream& out, const Date& d);
//Date.cpp

#include"Date.h"

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	// 检查日期是否合法
	if (month < 1 || month > 12
		|| day < 1 || day > GetMonthDay(year, month))
	{
		cout << "非法日期" << endl;
		// exit(-1);
	}
}

void Date::Print()
{
	cout << _year << "年" << _month << "月"  << endl;
}

void operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;
}
//Test.cpp

#include"Date.h"

int main()
{
	Date d1(2025, 1, 21);
	Date d2(2024, 2, 2);
	cout << d1 ;

	return 0;
}

//Test.cpp

#include"Date.h"

int main()
{
	Date d1(2025, 1, 21);
	Date d2(2024, 2, 2);
	cout << d1 << d2 ;

	return 0;
}

//Date.h

#pragma once

#include<iostream>
using namespace std;

class Date
{
	
	friend ostream& operator<<(ostream& out, const Date& d);

    Date(int year = 1, int month = 1, int day = 1);

public:

	void Print();

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

ostream& operator<<(ostream& out, const Date& d);
//Date.cpp

#include"Date.h"

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	// 检查日期是否合法
	if (month < 1 || month > 12
		|| day < 1 || day > GetMonthDay(year, month))
	{
		cout << "非法日期" << endl;
		// exit(-1);
	}
}

void Date::Print()
{
	cout << _year << "年" << _month << "月"  << endl;
}

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;

    return out;
}

//Date.h

#pragma once

#include<iostream>
using namespace std;

class Date
{
	// 友元声明
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);

    Date(int year = 1, int month = 1, int day = 1);

public:

	void Print();

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

ostream& operator<<(ostream& out, const Date& d);

//这里不能加const,因为这是在内部修改让外部拿到这个值
istream& operator>>(istream& in, Date& d);
//Date.cpp

#include"Date.h"

Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	// 检查日期是否合法
	if (month < 1 || month > 12
		|| day < 1 || day > GetMonthDay(year, month))
	{
		cout << "非法日期" << endl;
		// exit(-1);
	}
}

void Date::Print()
{
	cout << _year << "年" << _month << "月"  << endl;
}

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "/" << d._month << "/" << d._day << endl;

	return out;
}

istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;

	return in;
}
//Test.cpp

#include"Date.h"

int main()
{
	Date d1(2025, 1, 21);
	Date d2(2024, 2, 2);
	cout << d1 << d2;

	cin >> d1;
	cout << d1;

	return 0;
}


四、再谈构造函数

    1.初始化列表

   定义:以一个 冒号开始 ,接着是一个以 逗号分隔的数据成员列表 ,每个 " 成员变量 " 后面跟
一个 放在括号中的初始值或表达式。
class Date
{
public:
    Date(int year, int month, int day)
         : _year(year)
         , _month(month)
         , _day(day)
    {}

private:
    int _year;
    int _month;
    int _day;
};
class A
{
public:
	A(int a = 0)
		:_a(a)
	{}
private:
	int _a;
};
	
class Date
{
public:
	// 初始化列表是每个成员定义的地方
	// 不管你写不写,每个成员都要走初始化列表
	
	Date(int year, int month, int day, int& i)
		: _year(year)
		, _month(month)
		,_a(1)
		,_refi(i)
	{
		// 赋值
		_day = day;
	}
	
private:
	int _year;  // 每个成员声明
	int _month = 1 ;
	int _day = 1;
   // C++11支持给缺省值,这个缺省值给初始化列表
   // 如果初始化列表没有显示给值,就用这个缺省值
   // 如果显示给值了,就不用这个缺省值
   //这里_day会使用缺省值,而_month不会使用
	

	// 下面三个例子必须定义时初始化
	const int _x = 10;
	int& _refi;
	A _a;
};

注意:

   1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)。

   2.类中包含以下成员,必须放在初始化列表位置进行初始化:

       ①引用成员变量     ②const成员变量    ③自定义类型成员(且该类没有默认构造函数时)

   3. 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,
一定会先使用初始化列表初始化。

   4.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后

次序无关。

总结:

   能用初始化列表就用初始化初始化列表,但有些场景还是需要初始化列表和函数体混着用。


五、explicit关键字

        构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用
#include<iostream>
using namespace std;

class A
{
public:
	A(int i)
		//A(int i)
		:_a(i)
	{
		cout << "A(int i)" << endl;
	}

private:
	int _a;
};

int main()
{
	A aa1(1);

	// 单参数构造函数的隐式类型转换
	// 用2调用A构造函数生成一个临时对象,再用这个对象去拷贝构造aa2
	// 编译器会再优化,优化用2直接构造 

	A aa2 = 2;

	return 0;
}


A& ref1 = 2;

这里的问题原因是临时对象具有常性,ref不能引用,属于权限的放大,加上const就没有问题了。

const A& ref1 = 2;

#include<iostream>
using namespace std;

class B
{
public:
	B(int b1, int b2)
		//explicit B(int b1, int b2)
		:_b1(b1)
		, _b2(b2)
	{
		cout << "B(int b1, int b2)" << endl;
	}
private:
	int _b1;
	int _b2;
};

int main()
{

	// C++11 支持多参数的隐式类型转换
	B bb1(1, 1);
	B bb2 = { 2, 2 };
	const B& ref2 = { 3,3 };

	return 0;
}


class B
{
public:
	explicit B(int b1, int b2)
		:_b1(b1)
		, _b2(b2)
	{
		cout << "B(int b1, int b2)" << endl;
	}
private:
	int _b1;
	int _b2;
};

重点:

        explicit修饰构造函数,禁止类型转换
class A
{
public:
	explicit A(int i)
		:_a(i)
	{
		cout << "A(int i)" << endl;
	}

private:
	int _a;
};

class B
{
public:
	explicit B(int b1, int b2)
		:_b1(b1)
		, _b2(b2)
	{
		cout << "B(int b1, int b2)" << endl;
	}
private:
	int _b1;
	int _b2;
};

六、生命周期

#include<iostream>
using namespace std;

class A
{
public:
	explicit A(int i)
		:_a(i)
	{
		cout << "A(int i)" << endl;
	}

	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}

	~A()
	{
		cout << "~A()" << _a << endl;
	}
private:
	int _a;
};

int main()
{

	// 有名对象 特点:生命周期在当前局部域
	A aa6(6);

	// 匿名对象。特点:生命周期只在这一行
	A(7);

	return 0;
}


总结

        以上就是今天要讲的内容,本文仅仅简单介绍了c++的基础。

;