C++核心编程三(初始化列表、静态成员、this指针、成员函数与变量、友元、运算符重载)

2023-12-29 20:08:18


基于b站黑马c++视频做的笔记,仅供参考和复习!!!

初始化列表

//初始化列表
class Person
{
public:
	//传统初始化操作
	/*Person(int a, int b, int c){
		m_A = a;
		m_B = b;
		m_C = c;
	}*/

	//初始化列表初始化属性
	Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c){
	
	}
	int m_A;
	int m_B;
	int m_C;
};

void test01()
{
	//Person p(10, 20, 30);
	Person p(30,20,10);

	cout << "m_A = " << p.m_A << endl;
	cout << "m_B = " << p.m_B << endl;
	cout << "m_C = " << p.m_C << endl;
}

int main()
{
	test01();
}

在这里插入图片描述

类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为:对象成员,且在析构函数中,利用初始化列表的方法来赋值。

//类对象作为类成员
class Phone
{
public:
	Phone(string pName){
		cout << "Phone的构造函数调用" << endl;
		m_PName = pName;
	}

	string m_PName;
};

class Person
{
public:
	//Phone m_Phone = pName 即---> Phone m_Phone = "苹果MAX"  隐式转换法赋初值操作Person p4 = 10;
	Person(string name, string pName) :m_Name(name), m_Phone(pName){
		cout << "Person的构造函数调用" << endl;
	}

	//姓名
	string m_Name;
	//手机
	Phone m_Phone;		//Phone类对象 作为 Person类成员
};

//当其他类对象作为本类成员,构造时先构造类对象,再构造自身,而析构顺序与构造相反
void test01()
{
	Person p("张三", "苹果MAX");
	cout << p.m_Name << "拿着" << p.m_Phone.m_PName << endl;
}

int main()
{
	test01();
}

静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。
静态成员分为:

  • 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
class Person
{
public:
	static int m_A;
	
	//静态成员变量也是有访问权限的
private:
	static int m_B;
};

//类外初始化
int Person::m_A = 100;
int Person::m_B = 10;

void test01()
{
	Person p;
	cout << p.m_A << endl;

	Person q;
	q.m_A = 200;   //所有对象共享一份数据,
					//p1对象修改了值,其他对象如p去访问的就是修改的值
	cout << "q的m_A是:" << q.m_A << endl;				
	cout <<"p的m_A是:"<< p.m_A << endl;  //p.m_A访问静态成员变量的值  输出200
}

void test02()
{
	//静态成员变量 不属于某个对象上,所以对象都共享同一份数据
	//因此静态成员变量有两种访问方式

	//1、通过对象进行访问
	Person p;
	cout << p.m_A << endl;

	//2、通过类名进行访问
	cout << Person::m_A << endl;

	//cout << Person::m_B << endl; 类外无法访问私有的静态成员变量
}
int main()
{
	test01();
	//test02();
}

test01()输出结果:
在这里插入图片描述

  • 静态成员函数
    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量
class Person
{
public:
	//静态成员函数
	static void func(){
		m_A = 100; //静态成员函数可以访问静态成员变量  所有对象共有的
		//m_B = 200; //静态成员函数 不可以访问 非静态成员变量 
	                 //无法区分到底是那个对象的m_B的属性
		cout << "static void func的调用" << endl;
	}

	static int m_A; //静态成员变量  共享的
	int m_B; //非静态成员变量

	//静态成员函数也是有访问权限的
private:
	static void func2(){
		cout << "static void func2的调用" << endl;
	}
};
int Person::m_A = 0;

//两种访问方式
void test01()
{
	//1、通过对象访问
	Person p;
	cout << p.m_A << endl;	//类外赋值 int Person::m_A = 0;
	p.func();
	cout << p.m_A << endl;	//静态函数内赋值

	//2、通过类名访问
	Person::func();

	//Person::func2();  类外访问不到私有静态成员函数
}

int main()
{
	test01();
}

在这里插入图片描述

成员变量和成员函数分开存储

在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上。

//成员变量 和 成员函数 分开存储的
class Person
{
	int m_A; //非静态成员变量 属于类的对象上

	static int m_B; //静态成员变量 不属于类对象上 类中不占用内存

	void fun() {}  //非静态成员函数 不属于类对象上 大家类共享

	static void func() {} //静态成员函数 不属于类对象上 大家类共享
};
int Person::m_B = 0;

void test01()
{
	Person p;
	//空对象占用内存空间为:1
	//C++编译器会给每个空对象分配一个字节空间,是为了区别空对象占内存的位置
	//每个空对象也应该有一个独一无二的内存地址
	cout << "size of p = " << sizeof(p) << endl;
}

void test02()
{
	Person p;
	cout << "size of p = " << sizeof(p) << endl; //4个字节
}

int main()
{
	//test01();
	test02();		//注释掉 int m_A; 输出为空 1字节
}

this指针

this指针指向被调用的成员函数所属的对象。

class Person
{
public:
	Person(int age){	//有参构造函数
		//this指向的是被调用的成员函数 所属的对象
		this->age = age;
	}

	Person& PersonAddAge(Person &p){
		this->age += p.age;

		//this指向p2的指针,而*this解引用指向的就是p2这个对象
		return *this;
	}
	//若Person&改为Person则为值传递 此时*this指向的对象会进行
	//形如 Person p = p'的拷贝构造函数调用返回的则不是p2本体,而是其他对象,
	//但是输出的是p2.age,所以最终值为p2.age = 20
	
	int age;
};
//1、解决名称冲突
void test01()
{
	Person p1(18);
	cout << "p1的年龄:" << p1.age << endl;
}

//2、返回对象本身用*this
void test02()
{
	Person p1(10);
	Person p2(10);

	//链式编程思想
	p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
	cout << "p2的年龄为:" << p2.age << endl;
}

int main()
{
	//test01();
	test02();
}

空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性

//空指针调用成员函数
class Person
{
public:
	void showClassName(){
		cout << "this is Person class" << endl;
	}
	
	void showPersonAge(){
		//报错原因是因为传入的指针是为NULL 解决办法如下
		if (this == NULL){
			return;
		}
		cout << "age = " << m_Age << endl; //属性前面默认其实为this->m_Age,但当前this又指向空的值,所以发生错误
	}

	int m_Age;
};

void test01()
{
	Person * p = NULL;
	p->showClassName(); //空指针可以调用成员函数
	p->showPersonAge();
}

int main()
{
	test01();
}

const修饰成员函数

this指针的本质 是指针常量 指针的指向是不可以修改的
const Person * const this
在成员函数后面加const,修饰的是this指向以及指针指向的值都也不可以修改。

//常函数
class Person
{
public:
	Person(int x):m_A(x){};

	void showPerson() const
	{
		m_B = 100;
		//this->m_A = 100;
		//this = NULL; //this指针不可以修改指针的指向的
	}

	void func(){
		m_A = 100;
	}

	int m_A;

	mutable int m_B; //特殊变量,即使在常函数中,也可以修改这个值,加关键字mutable
};

// void test01()
// {
// 	Person p;
// 	p.showPerson();
// }

//常对象
void test02()
{
	const Person p(50); //在对象前加const,变为常对象 vscode中 常对象必须初始化 因此对代码进行了改进
	//p.m_A = 100;
	p.m_B = 200; //特殊变量 在常对象下可以修改

	//常对象只能调用常函数
	p.showPerson();
	//p.func(); //常对象 不可以调用普通成员函数,因为普通成员函数可以修改属性,而常对象不允许修改属性
}

int main()
{
	//test01();
	test02();
}

友元

友元的关键字为 friend
友元的目的就是让一个函数或者类 访问另一个类中私有成员。
友元的三种实现

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元
1、全局函数做友元
#include <iostream>
using namespace std;

//建筑类
class Building
{
	//goodGay全局函数是Building好朋友,可以访问Building中私有成员 即卧室
	friend void goodGay(Building* building);

public:
	Building(){ 		//构造函数
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}

public:
	string m_SittingRoom; //客厅
private:
	string m_BedRoom; //卧室
};

//全局函数
void goodGay(Building *building)
{
	cout << "好基友全局函数 正在访问:" << building->m_SittingRoom << endl;
	cout << "好基友全局函数 正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
	Building building;
	goodGay(&building);  //利用指针调用  还可以直接1、对象调用(值传递) 2、引用调用
}

int main()
{
	test01();
}

在这里插入图片描述

2、类做友元
//类做友元
class Building
{
	//GoodGay是Building好朋友,可以访问Building中私有成员
	friend class GoodGay;

public:
	Building();
public:
	string m_SittingRoom; //客厅

private:
	string m_BedRoom; //卧室
};

class GoodGay
{
public:
	GoodGay(); //构造函数
	void visit(); //参观函数,访问Building中的属性

	Building * building;
};

//类外写成员函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

GoodGay::GoodGay()
{
	//创建建筑物对象
	building = new Building;  //创建开辟一个Building类型的指针,返回该地址,building指向开辟一个内存地址
							  //且会调用Building中的构造函数
							  // 加括号调用有参构造函数,不加括号调用默认构造函数或唯一的构造函数,看需求
}

void GoodGay::visit()
{
	cout << "visit函数 正在访问:" << building->m_SittingRoom << endl;
	cout << "visit函数 正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
	GoodGay gg;	 //创建GoodGay对象后会马上调用GoodGay::GoodGay()构造函数
	gg.visit();
}
int main()
{
	test01();
}

在这里插入图片描述

3、成员函数做友元
class Building;	//类 声明
class GoodGay
{
public:

	GoodGay();
	void visit(); //让visit函数可以访问Building中的私有成员
	void visit2();//让visit函数不可以访问Building中的私有成员

	Building * building;
};

class Building
{
	//告诉编译器 GoodGay类下的visit成员函数作为本类的好朋友,可以访问私有成员
	friend void GoodGay::visit();

public:
	Building(); //构造函数

public:
	string m_SittingRoom; //客厅

private:
	string m_BedRoom; //卧室
};

//类外实现成员函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

GoodGay::GoodGay()
{
	//创建建筑物对象
	building = new Building;
}

void GoodGay::visit()
{
	cout << "visit函数 正在访问:" << building->m_SittingRoom << endl;
	cout << "visit函数 正在访问:" << building->m_BedRoom << endl;
}

void GoodGay::visit2()
{
	cout << "visit2函数 正在访问:" << building->m_SittingRoom << endl;
	//cout << "visit2函数 正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
	GoodGay gg;
	gg.visit();
	gg.visit2();
}

int main()
{
	test01();
}

加号运算符重载

运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
作用:实现两个自定义数据类型相加的运算。

//加号运算符重载 实现两个自定义数据类型相加的运算
class Person
{
public:

	//1、 成员函数重载+号
	/*Person operator+(Person& p)
	{
		Person temp;
		temp.m_A = this->m_A + p.m_A;
		temp.m_B = this->m_B + p.m_B;
		return temp;
	}*/

	int m_A;
	int m_B;
};

//2、全局函数重载+号
Person operator+(Person& p1, Person& p2)
{
	Person temp;
	temp.m_A = p1.m_A + p2.m_A;
	temp.m_B = p1.m_B + p2.m_B;
	return temp;
}

//函数重载的版本
Person operator+(Person &p1,int num)
{
	Person temp;
	temp.m_A = p1.m_A + num;
	temp.m_B = p1.m_B + num;
	return temp;
}

void test01()
{
	Person p1;
	p1.m_A = 10;
	p1.m_B = 10;

	Person p2;
	p2.m_A = 10;
	p2.m_B = 10;

	//1、成员函数重载本质调用
	//Person p3 = p1.operator+(p2);

	//2、全局函数重载本质调用
	//Person p3 = operator+(p1, p2);
	
	//由于使用编译器提供的函数名operator+ 可简化为下面这种形式调用
	Person p3 = p1 + p2;
	cout << "p3.m_A = " << p3.m_A << "\t p3.m_B = " << p3.m_B << endl;

	Person p4 = p1 + 100; //Person + int
	cout << "p4.m_A = " << p4.m_A << "\t p4.m_B = " << p4.m_B << endl;
}
int main()
{
	test01();
}

左移运算符重载

作用:可以输出自定义数据类型

//左移运算符重载 可以输出自定义数据类型
class Person
{
	friend ostream& operator<<(ostream& cout, Person& p);

public:
	Person(int a, int b){
		m_A = a;
		m_B = b;
	}
private:
	//利用成员函数重载 左移运算符 p.operator<<(cout) 简化 p << cout ;
	//不能利用成员函数重载<<运算符,因为无法实现cout在左侧
	/*int operator<<()
	{
	}*/

	 int m_A;
	 int m_B;
};

//只能利用全局函数重载左移运算符
ostream& operator<<(ostream & cout,Person & p)
{
	cout << "m_A = " << p.m_A << " m_B = " << p.m_B ;
	return cout;
}

void test01()
{
	Person p(10,10);
	//p.m_A = 10;
	//p.m_B = 11;

	cout << p << " hello world" << endl;
}

int main()
{
	test01();
}

递增运算符重载

#include <iostream>
using namespace std;

//重载递增运算符  实现自己的整型数据
//自定义整型
class MyInteger
{
	friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
	MyInteger(){
		m_Num = 0;
	}

	//重载前置++运算符  返回引用为了一直对一个数据进行递增操作
	MyInteger& operator++(){
		m_Num++;
		return *this;
	}

	//重载后置++运算符
	//void operator++(int) int代表占位参数,可以区分前置和后置递增
	MyInteger operator++(int){
		//先 记录当时结果
		MyInteger temp = *this; //保存了值传递里的值

		//后 递增
		m_Num++;

		//最后记录结果做返回
		return temp;
	}

private:
	int m_Num;

};

//重载<<运算符
ostream & operator<<(ostream & cout, MyInteger myint)
{
	cout << myint.m_Num;
	return cout;
}

void test01()
{
	MyInteger myint;
	
	cout << ++(++myint) << endl;
	cout << myint << endl;
}

void test02()
{
	MyInteger myint;

	cout << (myint++) << endl;
	cout << myint << endl;
}

int main()
{
	//test01();
	test02();
}

赋值运算符重载

#include <iostream>
using namespace std;

//赋值运算符重载

class Person
{
public:
	Person(int age){
		m_Age = new int(age); //在堆区开辟内存 类似于new int(18)
	}

	~Person(){
		if (m_Age != NULL){
			delete m_Age;
			m_Age = NULL;
		}
	}
	//重载 赋值运算符
	Person& operator=(Person& p){
		//编译器提供的是浅拷贝
		//m_Age= p.m_Age;

		//应该先判断是否有属性在堆区,如果有显示释放干净,然后再深拷贝
		if (m_Age != NULL){
			delete m_Age;
			m_Age = NULL;
		}

		//深拷贝
		m_Age = new int(*p.m_Age);

		//返回对象自身
		return *this;
	}
	int* m_Age;
};

void test01()
{
	Person p1(18);
	Person p2(20);
	Person p3(30);

	p3 = p2 = p1; //类对象的赋值操作

	cout << "p1的年龄为:" << *p1.m_Age << endl;
	cout << "p2的年龄为:" << *p2.m_Age << endl;
	cout << "p3的年龄为:" << *p3.m_Age << endl;
}

int main()
{
	test01();
}

关系运算符重载

//关系运算符重载
class Person
{
public:
	Person(string name, int age){
		m_Name = name;
		m_Age = age;
	}

	//重载 == 号
	bool operator==(Person& p){
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){
			return true;
		}
		return false;
	}

	string m_Name;
	int m_Age;
};

void test01()
{
	Person p1("张三", 18);
	Person p2("张三", 18);

	if (p1 == p2){
		cout << "p1和p2是相等的" << endl;
	}
	else{
		cout << "p1和p2是不相等的" << endl;
	}
}

int main()
{
	test01();
}

函数调用运算符重载

//函数调用符重载

//打印输出类
class MyPrint
{
public:
	//重载函数调用运算符
	void operator()(string test){
		cout << test << endl;
	}
};

void MyPrint02(string test)
{
	cout << test << endl;
}

void test01()
{
	MyPrint myPrint;
	myPrint("hello world"); //类作为函数调用 由于使用起来非常类似于函数调用,因此称为仿函数
	
	MyPrint02("hello world2"); //常规调用
}

//仿函数非常灵活,没有固定的写法
//加法类
class MyAdd
{
public:
	//重载函数调用运算符
	int operator()(int a, int b){
		return a + b;
	}
};

void test02()
{
	MyAdd add;
	cout << add(1, 3) << endl;

	//匿名函数对象  MyAdd()这种形式称为匿名对象,使用完后立即释放
	cout << MyAdd()(1, 6) << endl;
}

int main()
{
	//test01();
	test02();
}

文章来源:https://blog.csdn.net/qq_45009309/article/details/135249890
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。