C++类和对象下

Posted 蚍蜉撼树谈何易

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++类和对象下相关的知识,希望对你有一定的参考价值。

const修饰的成员函数

规则1:const的成员变量不可以去调用非const的成员函数
测试用例:

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

	~date()
	{

	}
	date* operator&()
	{
		day += 1;//在不加const的情况下,我们可以对this指针下的变量进行修改
		cout << this << endl;
		return this;
	}

private:

	int year;
	int month;
	int day;
};
void test()
{
	date m1(2010, 10, 25);
	date* p1 = &m1;
	const date m2(2020, 12, 15);
	const date* p2 = &m2;
}

在这里插入图片描述
原因是:避免了错误调用所带来的错误修改,与const修饰的初衷背道而驰。
规则2:非const的类对象可以调用const修饰的成员函数,但必须使用const类型的对象去接收。

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

	~date()
	{

	}
	const date* operator&()const
	{
	
		cout << this << endl;
		return this;
	}

private:

	int year;
	int month;
	int day;
};
void test()
{
	date m1(2010, 10, 25);
	const date* p1 = &m1;//可以使用非const对象去调用,但必须使用const类型对象指针去接收
	const date m2(2020, 12, 15);
	const date* p2 = &m2;
}

运行结果:
在这里插入图片描述
这里分析一下const成员函数这么写的原因
date * operator&(); 底层会传入一个 date* const this ;
const dateoperator& ()const;第一个const是为了修饰返回值的,避免就比如说我现在要这个对象的地址,我给你了,但是你用一个非const的对象指针去接收,这样你就可以对这里面的东西进行修改了,那我为什么要给你?所以这便是第一个const要加的原因。第二个const 是用来修饰this指针的,加上这个const,则该this指针变为了 const dateconst this //值不能改,指向也不能改的情况。

那是不是就不能做出修改了?假如说我不想变这个成员函数结构,又想对它中的某些变量进行修改,有没有解决办法:
multable关键字:mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。

规则三:只对返回值进行修改的const类的对象也不可以调用。
因为呢,此时你的意思就是我可以在这个成员函数里改我传入的this指针的值,但是呢,我更改后的返回值你不能对我再做出修改。

规则四:const修饰的成员函数内部只能调用const’修饰的成员函数,非const修饰的成员函数既可以调用const修饰的成员函数,也可以调用非const类的成员函数,类似于 权限缩小
注:构造函数,析构函数,拷贝构造,赋值运算符重载这四个函数可不可以用const修饰?
不能,因为需要对隐含的this指针进行修改。

const修饰成员变量

区分一下构造函数的初始化与赋值区别

初始化:只能初始化一次
赋值:可以进行多次的赋值

比如 const int a=10;//这就是初始化,因为他自创建起到它的生命周期结束,只可以使用这个值。
而int a=10; a=20;//这就是赋值,只要它定义了,并且它里面的内存没有被回收,就可以对它多次执行赋值的操作。
首先我们看赋值操作:
我们还是看date这个例子:
在这里插入图片描述
初始化
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式
在这里插入图片描述

const修饰成员变量

定义的两种方式:
在这里插入图片描述
在这里插入图片描述

类作为另一个类对象时调用构造函数

下面这个代码是没问题的,虽然只在初始化列表中初始化了date中的成员变量,但编译器会隐式调用time中的构造函数

class Time
{
public:
	Time()//time类的默认构造函数
	{
		hour = 0;
		minute = 0;
		second = 0;
	}
private:
	int hour;
	int minute;
	int second;
};
class date
{
public:
	date(int _year, int _month, int _day):year(_year),month(_month),day(_day)
	{
		//env = 10;
	}

	~date()
	{

	}
	void show()
	{
		cout << year << endl;
		cout << month << endl;
		cout << day << endl;
		
	
	}
private:

	int year;
	int month;
	int day;
	Time  _t;
	
};
void test01()
{ 
	date m1(2020, 8, 16);
	
}

这里我们换一种方式:

class Time
{
public:
	Time(int _hour,int _minute,int _second)
	{
		hour = _hour;
		minute = _minute;
		second = _second;
	}
private:
	int hour;
	int minute;
	int second;
};
class date
{
public:
	date(int _year, int _month, int _day):year(_year),month(_month),day(_day),_t(20,8,20)
	{
		//env = 10;
	}

	~date()
	{

	}
	void show()
	{
		cout << year << endl;
		cout << month << endl;
		cout << day << endl;
		
	
	}
private:

	int year;
	int month;
	int day;
	Time  _t;
	
};
void test01()
{ 
	date m1(2020, 8, 16);
	
}

在这里插入图片描述
在这里插入图片描述

构造函数的初始化列表中必须显式的给出三种情况

引用成员变量
const成员变量
自定义类型成员(该类没有默认构造函数)
注:尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化

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

class date
{
public:
	date(int _year, int _month, int _day) : day(_day),month(day), year(_year)
	{
		//env = 10;
	}

	~date()
	{

	}
	void show()
	{
		cout << year << endl;
		cout << month << endl;
		cout << day << endl;

	}
private:
	int year;
	int month;
	int day;
};
void test01()
{
	date m1(2020, 11, 20);
	m1.show();
}

在这里插入图片描述

静态成员变量与静态成员函数

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。**静态的成员变量 一定要在类外进行初始化
与一般的数据成员不同,无论建立了多少个对象,都只有一个静态数据的拷贝。静态成员变量,属于某个类,所有对象共享。静态变量,是在编译阶段就分配空间,对象还没有创建时,就已经分配空间。

静态成员变量

一些特性:
1.静态成员变量必须类外定义
在这里插入图片描述

2.静态成员变量为所有类对象共享,不属于某一个具体的实例
在这里插入图片描述

3.静态成员的访问
两种方式:通过类名直接访问 Person::sNum;//前提是它不为私有情况下
通过对象使用 假如说创建了个d1对象,可以 d1.sNum;还是不为私有的情况下。
这两种方法在底层处理是一样的,一般推荐使用第一种方法。

class Person{
public:
	//类的静态成员属性
	static int sNum;
private:
	static int sOther;
};

//类外初始化,初始化时不加static
int Person::sNum = 0;
int Person::sOther = 0;
int main(){


	//1. 通过类名直接访问
	Person::sNum = 100;
	cout << "Person::sNum:" << Person::sNum << endl;

	//2. 通过对象访问
	Person p1, p2;
	p1.sNum = 200;

	cout << "p1.sNum:" << p1.sNum << endl;
	cout << "p2.sNum:" << p2.sNum << endl;

	//3. 静态成员也有访问权限,类外不能访问私有成员
	//cout << "Person::sOther:" << Person::sOther << endl;
	Person p3;
	//cout << "p3.sOther:" << p3.sOther << endl;

	system("pause");
	return EXIT_SUCCESS;
}

静态成员函数

在类定义中,前面有static说明的成员函数称为静态成员函数。静态成员函数使用方式和静态变量一样,同样在对象没有创建前,即可通过类名调用。静态成员函数主要为了访问静态变量,但是,不能访问普通成员变量。
因为静态成员函数中不含有隐藏的this指针。

静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装。
特性:
1.static成员函数只能对static的成员变量或成员函数进行操作。本质原因:static函数体内部不含有隐藏的this指针,而普通成员函数中是有this指针的。
在这里插入图片描述
在这里插入图片描述

2.静态成员函数的访问方式与静态成员的访问方式相同。

class date
{
public:
	date(int _year, int _month, int _day):year(_year),month(_month),day(_day)
	{
		count += 1;
	
	}
	~date()
	{
		count -= 1;
	}
	static void show()
	{
		cout << count << endl;
	}
	
private:
	int year;
	int month;
	int day;
	static int count;
};
int date::count = 0;
void test()
{
	
	date m1(2020, 10, 20);
	date m2(2021, 11, 20);
	date m3(2022, 11, 20);
    //法1:通过类名+作用域限定符+静态成员函数名
	date::show();
	//法2:通过对象引用
	m3.show();
}

3.普通成员函数可访问静态成员变量、也可以访问非静态成员变量

class date
{
public:
	date(int _year, int _month, int _day):year(_year),month(_month),day(_day)
	{
		count += 1;
	
	}
	~date()
	{
		count -= 1;
	}
	static void show()
	{
		cout << count << endl;
		
	}
	void print()
	{
		show();
		cout << count << endl;
	}
	
	
private:
	int year;
	int month;
	int day;
	static int count;
};
int date::count = 0;
void test()
{
	
	date m1(2020, 10, 20);
	date m2(2021, 11, 20);
	date m3(2022, 11, 20);
 //   //法1:通过类名+作用域限定符+静态成员函数名
	//date::show();
	法2:通过对象引用
	//m3.show();
	m3.print();
}

在这里插入图片描述
4.静态成员函数也会受到权限的约束
在这里插入图片描述

重载<<运算符输出对象

在类内定义:

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

	}
	//version 1: <<重载规则:1.第一个参数必须为ostream& 2.第二个参数才是打印的内容
	void operator<<(ostream& _cout)
	{
		_cout << year << "   " << month << "  " << day << endl;
	}
private:
	int year;
	int month;
	int day;

};
void test01()
{
	date m1(2020, 11, 20);
	//m1 << cout;
	m1.operator<<(cout);

}

在这里插入图片描述

在类外写方法:(推荐)

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

	}
	int gety()const
	{
		return year;
	}
	int getm()const
	{
		return month;
	}
	int getd()const
	{
		return day;
	}
	
private:
	int year;
	int month;
	int day;

};
//version 2:在类外重载   弊端:d中的成员为私有的,所以得调用接口去获取到d中私有变量的值
ostream& operator<<(ostream& _cout,  const date& d)
{
	_cout << d.gety() <<"  "<< d.getm() << "  "<< d.getd() << endl;
	return _cout;
}
void test()
{
	date m1(2020, 11, 20);
	date m2(2021, 9, 15);
	cout << m1<<m2;

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

在这里插入图片描述

友元

友元函数

现在我们尝试去重载operator<<,然后发现我们没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以我们要将operator<<重载成全局函数。但是这样的话,又会导致类外没办法访问成员,那么这里就需要友元来解决。
友元函数概念:
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
看我们上面的version2,此时虽然可以打印的,但是类里面的成员变量的值我们必须定义相应的接口才可获取到,会造成时间上与空间上的浪费,这时候我们采用友元函数来实现。

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

	}
	friend  ostream& operator<<(ostream& _cout, const date& d);
private:
	int year;
	int month;
	int day;

};
//version 3:在类外重载,利用友元函数   
ostream& operator<<(ostream& _cout, const date& d)
{
	//_cout << d.gety() << "  " << d.getm() << "  " << d.getd() << endl;
	cout << d.year << "   " << d.month << "   " << d.day << endl;
	return _cout;
}
void test()
{
	date m1(2020, 11, 20);
	date m2(2021, 9, 15);
	cout << m1 << m2;

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

在这里插入图片描述
特性:
友元函数可访问类的私有和保护成员,但不是类的成员函数

友元函数不能用const修饰
因为const修饰的是this,友元函数不属于成员函数,所以不能用const修饰。

友元函数可以在类定义的任何地方声明,不受类访问限定符限制

一个函数可以是多个类的友元函数

友元函数的调用与普通函数的调用和原理相同

友元类

特性:

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

class Date; // 前置声明
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成
员变量
public:
Time(int hour, int minute, int second)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
C++初阶第六篇——类和对象(下)(初始化列表+explicit关键字+static成员+友元+内部类)

《c++从0到99》 四 类和对象 下

《c++从0到99》 四 类和对象 下

C++初阶第四篇——类和对象(上)(类的定义+封装+this指针)

C++入门基础教程:类和对象(下)

C++学习之旅第二站:类和对象进阶