C++ 之路如逆水行舟 不进则退

发布时间:2024年01月19日

C++ 核心编程

内存分区模型

C++ 程序在执行时,将内存大方向划分为 4 个区域
代码区:存放函数体的二进制代码,由操作系统进行管理的
全局区:存放全局变量和静态变量以及常量
栈区:由编译器自动分配释放 , 存放函数的参数值 , 局部变量等
堆区:由程序员分配和释放 , 若程序员不释放 , 程序结束时由操作系统回收
内存四区意义:灵活,执行效率快,方便管理
不同区域存放的数据,赋予不同的生命周期 , 给我们更大的灵活编程

1.1 程序运行前

在程序编译后,生成了 exe 可执行程序, 未执行该程序前 分为两个区域
代码区:共享,app多次可以大概,只读,不能修改内容
存放 CPU 执行的机器指令
代码区是 共享 的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码区是 只读 的,使其只读的原因是防止程序意外地修改了它的指令
全局区:
全局变量和静态变量存放在此 .
全局区还包含了常量区 , 字符串常量和其他常量也存放在此 .
== 该区域的数据在程序结束后由操作系统释放 ==.
总结:
C++ 中在程序运行前分为全局区和代码区
代码区特点是共享和只读
全局区中存放全局变量、静态变量、常量
常量区中存放 const 修饰的全局常量 和 字符串常量

#include <iostream>
using namespace std;

// 1.3全局变量global全球
int g_a = 10;
int g_b = 10;
// const 修饰全局变量
const int g_c = 10;
int main() {
	// 1.全局区分 全局变量、静态变量、常量(字符串常量、const修饰的全局变量)

	// 1.1局部变量
	int a = 10;
	int b = 10;
	cout << "局部变量a的地址:" << (int)&a << endl;
	cout << "局部变量b的地址:" << (int)&b << endl;

	// 1.2静态变量
	static int s_a = 10;
	static int s_b = 10;
	cout << "静态变量s_a的地址:" << (int)&s_a << endl;
	cout << "静态变量s_b的地址:" << (int)&s_b << endl;

	// 1.3 打印全局变量地址
	cout << "全局变量g_a的地址:" << (int)&g_b << endl;
	cout << "全局变量g_b的地址:" << (int)&g_b << endl;

	// 1.4常量(字符串常量、const常量)
	cout << "字符串常量的地址:" << (int)&"hello world" << endl;
	cout << "const修饰全局变量g_c的地址:" << (int)&g_c << endl;

	const int l_a = 10;
	cout << "const 修饰局部变量l_a的地址:" << (int)&l_a << endl;


	system("pause");
	return 0;
}

程序运行后

栈区:
由编译器自动分配释放 , 存放函数的参数值 , 局部变量等
注意事项:不 要返回局部变量的地址,栈区开辟的数据由编译器自动释放
#include <iostream>
using namespace std;

int* func() {
	int a = 10;
	return &a;
}
int main() {
	// 1.函数调用 栈 用完及释放,不要通过栈来返回地址空间
	int* p = func();
	cout << p << endl;
	cout << *p << endl;	//打印值会乱掉,因为函数用完即释放了

	system("pause");
	return 0;
}

堆区:
由程序员分配释放 , 若程序员不释放 , 程序结束时由操作系统回收
C++ 中主要利用 new 在堆区开辟内存
总结:
堆区数据由程序员管理开辟和释放
堆区数据利用 new 关键字进行开辟内存

new操作符

C++ 中利用 ==new== 操作符在堆区开辟数据
堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符 ==delete==
语法: new 数据类型
利用 new 创建的数据,会返回该数据对应的类型的指针

#include <iostream>
using namespace std;

int* func() {
	//int* p = new int(10);//new在堆中创建一个变量返回地址

	return new int[10];
}

// 1.1 new创建一个变量
void test01()
{
	int* p = func();
	cout << p << endl;
	cout << *p << endl;
	cout << *p << endl;
	delete(p);			//释放 堆区上的p指向地址中的内容
	cout << *p << endl; // 访问不到
}

//new创建一个数组
void test02() {
	int* arr = func();//func中返回new的一个数组

	for (int i = 0; i < 10; i++) {
		arr[i] = i + 100;
	}
	for (int i = 0; i < 10; i++) {
		cout << arr[i] << endl;;
	}

	delete[] arr;	//释放堆区数组

}

int main() {
	// 1.在堆中创建一个变量后释放 用new创建返回地址
	//test01();

	// 2.用new创建一个数组
	test02();

	system("pause");
	return 0;
}

?引用

作用: 给变量起别名
语法: 数据类型 & 别名 = 原名
引用必须初始化
引用在初始化后,不可以改变

#include <iostream>
using namespace std;

int main() {
	// 1.是什么引用 已存在变量的别名

	// 2.定义格式  数据类型 &引用名 = 变量名;都可以改变变量
	int a = 10;
	int& qa = a;	//引用初始化 之后不可改变

	int* p = &a;
	cout << "a = " << a << endl;	
	cout << "a = " << qa << endl;	//引用
	cout << "a = " << *p << endl;	//指针

	// 3.指针和引用的区别:
	// 引用就是换个别名且不占用内存,和原来是同一个东西,引用初始化后不能改变,不存在空引用
	// 而指针是变量,占用空间存储的是一个地址,初始化后可以改变指向,指针可以指向空

	system("pause");
	return 0;
}

?引用必须初始化、且初始化之后不能改变指向

#include <iostream>
using namespace std;

int main() {
	
	// 1.引用必须初始化
	int a = 10;
	int& ya = a; // 引用必须初始化

	// 2.引用初始化之后不能改变
	int c = 20;
	ya = c;		// 意思是吧c赋值给a
	cout << "a = " << a << endl;
	cout << "ya = " << ya << endl;
	cout << "c = " << c << endl;

	system("pause");
	return 0;
}

函数的三种传递方式(值传递、地址、引用)?

总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单
#include <iostream>
using namespace std;

// 1.1值传递
void Swap01(int a, int b) {
	int temp;
	temp = a;
	a = b;
	b = temp;
	cout << "Swap01 a = " << a << endl;
	cout << "Swap01 b = " << b << endl;
}

// 1.2地址传递
void Swap02(int* a, int* b) {
	int temp;
	temp = *a;
	*a = *b;
	*b = temp;
	cout << "Swap02 a = " << *a << endl;
	cout << "Swap02 b = " << *b << endl;
}

// 1.3引用传递	//根据main中swap(a,b)这边的int& a引用和a一样
void Swap03(int& a, int& b) {
	int temp;
	temp = a;
	a = b;
	b = temp;
	cout << "Swap03 a = " << a << endl;
	cout << "Swap03 b = " << b << endl;
}

int main() {
	// 1.函数的三种传递方式
	int a = 10;
	int b = 20;

	Swap03(a,b);//交换函数用引用接收 可以改变
	cout << "main a = " << a << endl;
	cout << "main b = " << b << endl;

	system("pause");
	return 0;
}

引用函数做返回值

作用:引用可以做函数的返回值存在

注意:不要返回局部变量的引用

用法:用函数作为左值------返回一个引用,用int& ret接收,也可以通过赋值改变

#include <iostream>
#include "08 引用函数做左值必须是引用.h"
using namespace std;

// 1.1不要返回局部变量的引用
int& test01() {
	int a = 10;
	return a;
}
// 1.2返回静态变量的引用
int& test02() {
	static int a = 10;//静态变量保存在全局区,不会随函数调用而消失
	return a;
}
// 1.3如果函数做左值,必须是引用才能返回
int& test03() {
	static int a = 10;
	return a;
}


int main() {
	// 1.返回引用,则用引用接收  引用和函数返回值之间的关系
	int& ret = test03();
	cout << "ret = " << ret <<endl;
	cout << "ret = " << ret <<endl;

	test03() = 1000;		//把1000赋值给 返回a的引用,而ret就是a的引用,
	cout << "ret = " << ret << endl;
	cout << "ret = " << ret << endl;

	system("pause");
	return 0;
}
结论: C++ 推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我 们做了
引用起始就是? int* const a;的指针常量,可以修改值不能修改指向,在内部编译器已经替我们优化了,作用是方便操作简化指针。

?常量引用

作用: 常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加 ==const 修饰形参 == ,防止形参改变实参
#include <iostream>
using namespace std;

void  showVelue(const int& b) {
	//b = 100;
	cout << "b = " << b << endl;
}
int main() {
	// 1.引用序号合法空间
	//int& a = 10;//错误,因为引用的地址空间得和常量一起,需要合法的地址空间

	// 2.const int& a = 10; 可以是因为编译器优化了把int temp = 10省略了
	const int& a = 10;
	cout << "a = " << a << endl;

	// 3.const之后不能修改
	//a = 20;错误,const修饰之后不能修改

	// 4.函数中常用常量引用 防止误操作修改实参
	int b = 20;
	showVelue(b);
	cout << "b = " << b << endl;

	system("pause");
	return 0;
}

函数提高

函数默认参数

C++ 中,函数的形参列表中的形参是可以有默认值的。
语法: 返回值类型 函数名 (参数 = 默认值) {}
#include <iostream>
using namespace std;

// 1.1普通传递
int demo01(int a, int b) {
	return a + b;
}

// 1.2有实参传入则按实参计算,没有则按形参
int demo02(int a, int b = 20, int c = 30) {
	return a + b + c;
}

// 1.3形参有默认值时从左往右必须依次有默认值
//int demo03(int a, int b = 10, int c) {		//错误案例
//	return a + b + c;
//}

// 1.4 函数声明和定义中的参数只能有其中一个有值,且最重还是以实参为准
int demo04(int a, int b = 20, int c = 30);

int demo04(int a, int b, int c) {
	return a + b + c;
}
int main() {
	// 1.函数的默认参数  返回值类型 函数名(参数1、参数2){};
	cout << demo02(10) << endl;		//输出60
	cout << demo02(10,30) << endl;  //输出70

	//cout << demo03(10,20) << endl;

	cout << demo04(30) << endl; 
	system("pause");
	return 0;
}

//1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
//2. 如果函数声明有默认值,函数实现的时候就不能有默认参数

函数占位参数

C++ 中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置

#include <iostream>
using namespace std;

// 普通占位符
void demo01(int a,int ) {
	cout << "this is demo01" << endl;
}

// 有参数的占位符
void demo02(int a, int = 20) {
	cout << "this is demo02" << endl;
}

int main() {
	// 1.函数占位参数
	
	// 1.1普通占用
	demo01(10,20);

	// 1.2当占用符有默认参数时
	demo02(10);

	system("pause");
	return 0;
}

函数重载

3.3.1 函数重载概述
作用: 函数名可以相同,提高复用性
函数重载满足条件:
同一个作用域下 函数名称相同
函数参数类型不同 或者 个数不同 或者 顺序不同
注意 : 函数的返回值不可以作为函数重载的条件
#include <iostream>
using namespace std;

// 2.1无参数
void func() {
	cout << "this is func......" << endl;
}
// 2.2有单个参数
void func(int a) {
	cout << "this is func......int" << endl;
}

// 2.3单个参数类型不同
void func(double a) {
	cout << "this is func......double" << endl;
}

// 2.4参数个数不同
void func(double a, int b) {
	cout << "this is func......double,int" << endl;
}

// 2.5参数顺序不同
void func(int a, double b) {
	cout << "this is func......int,double" << endl;
}

int main() {
	// 1.函数重载作用:函数名相同,提高复用性
	// 2.条件:同一个作用域下、函数名相同、函数参数类型不同或者个数不同或者顺序不同
	func(5.15);
	func(8, 8.88);

	system("pause");
	return 0;
}

函数重载注意事项
引用作为重载条件
函数重载碰到函数默认参数
#include <iostream>
using namespace std;

// 1.1引用和const的重载函数
void demo01(int& a) {
	cout << "this is &a" << endl;
}

void demo01(const int& a) {
	cout << "this is const &a" << endl;
}


// 2.1重载函数遇上参数时
void test01(int a) {
	cout << "this is test01 ....int" << endl;
}

void test01(double a) {
	cout << "this is test01 ....double" << endl;
}

void test02(int a,int b) {
	cout << "this is test02 ....int,int" << endl;
}

void test02(int a) {
	cout << "this is test02 ....int" << endl;
}


// 3.1函数重载遇上有默认参数时
void test03(int a,int b = 20) {
	cout << "this is test03 ....int,int" << endl;
}

void test03(int a) {
	cout << "this is test03 ....double" << endl;
}

int main() {
	// 1.函数重载:引用作为重载条件
	int a = 10;
	demo01(a);		//demo01中引用&a 引用是可读可写的所以传入a
	demo01(10);		//demo01中const int& a是修饰a是只读的 所以只能传入数字

	// 2.函数重载,遇上默认参数
	//test02(10,20);
	//test02(10);

	// 3.函数重载遇上有默认参数
	//test03(15);出错因为编译器不知道走那个函数
	test03(10, 20);//没毛病

	system("pause");
	return 0;
}

?类和对象

封装意义一:
在设计类的时候,属性和行为写在一起,表现事物
语法: class 类名 { 访问权限: 属性 / 行为 } ;
封装意义二:
类在设计时,可以把属性和行为放在不同的权限下,加以控制

初创学生类

#include <iostream>
#include "string.h"

using namespace std;
/*
成员:类中得属性和行为  统称----成员
属性:成员属性、成员变量
行为:成员函数、成员方法
*/
// 1.1创建学生类
class Student {
public:
	//属性
	string m_Name;
	int    m_Age;

	//通过传入参数赋值给name
	void setName(string name) {
		m_Name = name;
	}

	void setAge(int age) {
		m_Age = age;
	}

	//方法
	void showMessage() {
		cout << "姓名:" << m_Name;
		cout << "  年龄:" << m_Age << endl;
	}
};

int main() {
	// 1.创建学生类
	Student s1;
	Student s2;
	Student s3;

	// 2.实例化学生类 并完善属性
	s1.m_Name = "张三";
	s1.m_Age = 18;
	s1.showMessage();

	s2.m_Name = "李四";
	s2.m_Age = 20;
	s2.showMessage();

	s3.setName("王五");
	s3.setAge(16);
	s3.showMessage();

	system("pause");
	return 0;
}

访问权限有三种

1. public 公共权限
2. protected 保护权限
3. private 私有权限
#include <iostream>
#include "string.h"
using namespace std;
/*
三种访问权限:
	公共权限public :   成员 类内可访问 类外可用访问
	保护权限protected: 成员 类内可访问 类外不能访问(儿子可用访问付清保护的内容)
	私有权限private		成员 类内可访问 儿子不可以访问父亲的私有内容 
*/

class Person
{
public:		//公共成员
	string m_Name;

protected:	//受保护的成员
	string m_Car;

private:	//私有的成员
	int m_Password;

public:		//公共的方法、函数
	void func() {
		m_Name = "张三";
		m_Car = "比亚迪";
		m_Password = 123456;
		cout << "姓名:" << m_Name;
		cout << " 小车:" << m_Car;
		cout << "  密码:" << m_Password << endl;
	}
};

int main() {
	// 1.实例化这个类
	Person p1;
	p1.m_Name = "王五";
	p1.func();

	system("pause");
	return 0;
}

?struct和class的区别

2 struct class 区别
C++ struct class 唯一的 区别 就在于 默认的访问权限不同
区别:
struct 默认权限为公共
class 默认权限为私有
#include <iostream>
#include "string.h"
using namespace std;
/*
三种访问权限:
	公共权限public :   成员 类内可访问 类外可用访问
	保护权限protected: 成员 类内可访问 类外不能访问(儿子可用访问付清保护的内容)
	私有权限private		成员 类内可访问 儿子不可以访问父亲的私有内容
*/

class c1
{
	int a;
};

struct c2
{
	int a;
};

int main() {
	// struct和class的区别
		// 1.1 struct是公有的相当于public都可以访问
		// 1.2 class默认是private私有的默认不可以访问

	c1 cc1;
	//c.a = 10; 默认私有不能访问

	c2 cc2;		//默认公有可以访问
	cc2.a = 100;
	cout << cc2.a << endl;

	system("pause");
	return 0;
}

成员属性设置为私有

优点 1 将所有成员属性设置为私有,可以自己控制读写权限
优点 2 对于写权限,我们可以检测数据的有效性

立方体 小案例 一

#include <iostream>
#include "string.h"
using namespace std;
/*
三种访问权限:
	公共权限public :   成员 类内可访问 类外可用访问
	保护权限protected: 成员 类内可访问 类外不能访问(儿子可用访问付清保护的内容)
	私有权限private		成员 类内可访问 儿子不可以访问父亲的私有内容
*/

class Cube
{
public:
	//设置长
	void set_w_l(int l) {
		w_l = l;
	}
	//获取长
	int get_w_l() {
		return w_l;
	}
	//设置宽
	void set_w_w(int w) {
		w_w = w;
	}
	//获取宽
	int get_w_w() {
		return w_w;
	}
	//设置高
	void set_w_h(int h) {
		w_h = h;
	}
	//获取高
	int get_w_h() {
		return w_h;
	}

	int getMianJi() {
		return 2 * w_l * w_w + 2 * w_l * w_h + 2 * w_w * w_h;
	}

	int getTiJi() {
		return w_l * w_w * w_h;
	}
	bool compare2(Cube& c) {
		if (w_l == c.get_w_l() && w_w == c.get_w_w() && w_h == c.get_w_h()) {
			return true;
		}
		else {
			return false;
		}
	}
private:    //私有属性
	int w_l;//长
	int w_w;//宽
	int w_h;//高

};

//全局函数对比两个是否相等
bool compare(Cube& c1, Cube& c2)
{
	if (c1.get_w_l() == c2.get_w_l() && c1.get_w_w() == c2.get_w_w() && c1.get_w_h() == c2.get_w_h()) {
		return true;
	}
	else {
		return false;
	}
}
int main() {

	// 1.创建类 (属性、方法)
	Cube c1;
	Cube c2;

	// 2.实例化求面积体积
	c1.set_w_l(10);
	c1.set_w_w(10);
	c1.set_w_h(10);
	cout << "c1的面积是: " << c1.getMianJi() << endl;
	cout << "c1的体积是: " << c1.getTiJi() << endl;

	c2.set_w_l(10);
	c2.set_w_w(10);
	c2.set_w_h(10);
	cout << "c2的面积是: " << c2.getMianJi() << endl;
	cout << "c2的体积是: " << c2.getTiJi() << endl;

	// 3.全局函数对比两个立方体是否相等
	int retValue;
	retValue = compare(c1,c2);
	if (retValue == true) {
		cout << "在全局函数中 这两个立方体相等!!" << endl;
	}
	else {
		cout << "在全局函数中 这两个立方体不相等!!" << endl;
	}

	// 4.成员函数对比两个立方体是否相等
	int retValue2;
	retValue2 = c1.compare2(c2);
	if (retValue2 == true) {
		cout << "在成员函数中 这两个立方体相等!!" << endl;
	}
	else {
		cout << "在成员函数中 这两个立方体不相等!!" << endl;
	}

	system("pause");
	return 0;
}

点和圆关系 案例二

#include <iostream>
#include "string.h"
using namespace std;
/*
三种访问权限:
	公共权限public :   成员 类内可访问 类外可用访问
	保护权限protected: 成员 类内可访问 类外不能访问(儿子可用访问付清保护的内容)
	私有权限private		成员 类内可访问 儿子不可以访问父亲的私有内容
*/

// 1.1创建点类 xy坐标,属性私有 通过方法访问
class Dot
{
public:
	//设置x
	void set_m_x(int x) {
		m_x = x;
	}
	//获取x
	int get_m_x() {
		return m_x;
	}
	//设置y
	void set_m_y(int y) {
		m_y = y;
	}
	//获取x
	int get_m_y() {
		return m_y;
	}
private:
	int m_x;
	int m_y;
};

// 2.1创建圆类  属性私有 通过方法访问
class Circle 
{
public:
	//设置点 传过来一个点赋值给圆的点属性
	void setDot(Dot dot) {
		myDot = dot;
	}
	//获取点,点包括了点的xy坐标
	Dot getDot() {
		return myDot;
	}
	//设置半径
	void set_m_r(int r) {
		m_r = r;
	}
	//获取半径
	int set_m_r() {
		return m_r;
	}
private:
	//点
	Dot myDot;

	//半径
	int m_r;
};

//通过圆上圆心坐标和传过来点的坐标平和  和 半径比较
void compareDotInCircle(Circle& cir, Dot& dot) {
	int disValue;
	disValue = (cir.set_m_r() * cir.set_m_r()) -
		((cir.getDot().get_m_x() - dot.get_m_x() ) * (cir.getDot().get_m_x() - dot.get_m_x()) +
		(cir.getDot().get_m_y() - dot.get_m_y()) * (cir.getDot().get_m_y() - dot.get_m_y()));
	if (disValue > 0) {
		cout << "在圆内!!" << endl;
	}
	else if (disValue == 0) {
		cout << "在圆上!!" << endl;
	}
	else {
		cout << "在圆外!!" << endl;
	}
}
int main() {
	// 1.创建点类
	// 1.1实例化一个点及点的坐标
	Dot dot;
	dot.set_m_x(10);
	dot.set_m_y(9);

	// 2.创建圆类
	// 2.1实例化一个圆,并创建一个圆心的点坐标,和半径长度
	Circle cir;
	Dot cirInDot;
	cirInDot.set_m_x(10);
	cirInDot.set_m_y(0);
	cir.setDot(cirInDot);
	cir.set_m_r(10);

	// 3.比较两个关系,通过圆的半径 和 圆上两点的距离来比较(即圆心和点的距离)
	compareDotInCircle(cir, dot);

	system("pause");
	return 0;
}

构造函数和析构函数

对象的 初始化和清理 也是两个非常重
c++ 利用了 构造函数 析构函数 解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和 清理工作。
对象的初始化和清理工作是编译器强制要我们做的事情,因此如果 我们不提供构造和析构,编译器会提 编译器提供的构造函数和析构函数是空实现。
构造函数语法: 类名 (){} 1. 构造函数,没有返回值也不写 void
2. 函数名称与类名相同
3. 构造函数可以有参数,因此可以发生重载
4. 程序在调用对象时候会自动调用构造,无须手动调用 , 而且只会调用一次
析构函数语法: ~ 类名 (){}
1. 析构函数,没有返回值也不写 void
2. 函数名称与类名相同 , 在名称前加上符号 ~
3. 析构函数不可以有参数,因此不可以发生重载
4. 程序在对象销毁前会自动调用析构,无须手动调用 , 而且只会调用一次
#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:
	
	~Person() {
		cout << "Person 这是析构函数" << endl;
	}
	Person() {
		cout << "Person 这是构造函数" << endl;
	}


};

void demo() {
	Person p;
}
int main() {
	// 1.构造函数和析构函数
	// 2.相当于初始化函数、和清理函数
	// 3.都只调用一次开始和结束时
	// 4.如果没有自己写则默认为空操作
	// 5.都只执行一次
	// 6.创建规则在class类中 构造函数 和类名字一样 类名{} 不用加void  析构函数则是加 ~类名{}
	// 7.最先执行构造函数

	demo();

	system("pause");
	return 0;
}

构造函数的分类和调用

两种分类方式:
按参数分为: 有参构造和无参构造
按类型分为: 普通构造和拷贝构造
三种调用方式: 括号法 显示法 隐式转换法
#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:
	int age;
	//无参构造函数
	Person() {
		cout << "这是无参构造函数" << endl;
	}
	//有参构造函数
	Person(int a) {
		cout << "这是有参构造函数" << endl;
		age = a;
	}
	//有参拷贝函数 不修改 引用
	Person(const Person& p) {
		cout << "这是拷贝函数" << endl;
		age = p.age;
	}
	//析构函数
	~Person() {
		cout << "这是析构函数" << endl;
	}
};

void demo() {
	// 2.1括号法(无参的不能加括号 否则会以为是函数声明)
	//Person p;		//无参构造函数不能加括号
	//Person p1(10);	
	//Person p2(20);

	// 2.2显示法
	Person p1;
	Person p2 = Person(10);//有参构造,通过P2 指向这个有参构造函数
	Person p3 = Person(p2);//拷贝构造,通过这个拷贝函数把P2这个类传过去(不可修改const Person& p)进行赋值
	Person(20);//匿名有参构造函数,不确定该对象,所以当前行执行结束就析构函数了

	// 2.3 隐式转换法
	Person p4 = 10;//相当于 Person p4(10)
	Person p5 = 20;//相当于 Person p5(20)
}
int main() {
	// 1.构造函数分类:分为有参构造函数、无参构造函数


	// 2.调用3种:
	// 2.1括号法(无参的不能加括号 否则会以为是函数声明)
	// 2.2显示法
	// 2.3 隐式转换法

	demo();

	system("pause");
	return 0;
}

?

拷贝构造函数调用时机

C++ 中拷贝构造函数调用时机通常有三种情况
使用一个已经创建完毕的对象来初始化一个新对象
值传递的方式给函数参数传值
以值方式返回局部对象

#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:
	Person() {
		cout << "这是无参构造函数" << endl;
	}
	Person(int a) {
		m_age = a;
		cout << "这是有参构造函数 age = "<< m_age << endl;
	}
	Person(const Person& p) {
		m_age = p.m_age;
		cout << "这是拷贝构造函数 age = " << p.m_age << endl;
	}
	~Person() {
		cout << "这是析构函数" << endl;
	}
	int m_age;
};

// 1.1使用创建对象初始化另一个对象
void demo01() {
	//Person p;		//无参构造函数
	Person p1(10);  //有参构造函数
	Person p2(p1);  //拷贝构造函数
}

// 1.2值传递方式
void DoWork(Person p) {

}
void demo02() {
	Person p(15);
	DoWork(p);
}

// 1.3以值返回 局部对象传递的是值而不是地址和引用,所以在Person p中返回接收的是新的栈,是拷贝函数,且地址不同
Person DoWork2() {
	Person p(10);
	cout << "地址:" << (int*) & p << endl;
	return std::move(p); // 使用std::move()显式移动对象
}

void domo03() {
	Person p2 = DoWork2();
	cout << "地址:" <<(int*) & p2 << endl;
}
int main() {
	// 1.拷贝构造函数调用 通常用三种
	// 1.1使用已经创建完毕的对象来初始化一个新对象
	domo03();

	// 1.2值传递的方式给函数传值

	// 1.3以值方式返回局部对象

	system("pause");
	return 0;
}

构造函数调用规则

默认情况下, c++ 编译器至少给一个类添加 3 个函数
1 .默认构造函数 ( 无参,函数体为空 )
2 .默认析构函数 ( 无参,函数体为空 )
3 .默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
如果用户定义有参构造函数, c++ 不在提供默认无参构造,但是会提供默认拷贝构造
如果用户定义拷贝构造函数, c++ 不会再提供其他构造函数

#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:
	int m_Age;
	//无参
	//Person() {
	//	cout << "无参构造函数" << endl;
	//}
	//有参
	//Person(int age) {
	//	m_Age = age;
	//	cout << "有参构造函数" << endl;
	//}
	// 
	//拷贝
	Person(const Person& p) {
		m_Age = p.m_Age;
		cout << "拷贝函数" << endl;
	}
	//析构
	~Person() {
		cout << "析构函数函数" << endl;
	}
};

//void demo01() {
//
//	Person p1;
//	p1.m_Age = 18;
//	Person p2(p1);	//把自己写的拷贝函数注释掉 则会调用c++中生成的拷贝函数,进行值拷贝
//	cout << "p2.m_Age = " << p2.m_Age << endl;
//}

//void demo02() {
//	Person p1(28);
//	Person p2(p1);
//	cout << "p2.m_Age = " << p2.m_Age << endl;
//	Person p3;		// 1.2当定义有参构造函数时,默认构造函数则不提供了  但是任有拷贝函数
//}

void demo03() {	//有定义一个拷贝函数 则不提供默认有参无参构造函数
	Person p;
}
int main() {
	// 1构造函数调用规则

	// 1.1默认有 构造、析构、拷贝函数
	//demo01(); //调用默认拷贝函数值拷贝


	// 1.2当有定义 有参构造函数时,就不提供无参构造函数 但是有拷贝函数
	//demo02(); 
	
	// 1.3当有定义 拷贝函数是,不提供其它函数
	demo03();//不提起默认的有参无参构造函数,直接调用无参构造函数出错
	system("pause");
	return 0;
}

利用构造函数初始化列表 ---- 初始化类中的属性

作用: C++提供了初始化列表语法,用来初始化属性
语法: 构造函数 () :属性 1( 1), 属性 2 (值 2 ... {}

#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:
	int m_a;
	int m_b;
	int m_c;
	// 通过构造函数列表初始化属性
	Person(int a,int b,int c):m_a(a),m_b(b),m_c(c) {

	}

	//void GetMessage(int a, int b, int c) {
	//	m_a = a;
	//	m_b = b;
	//	m_c = c;
	//}

	void printfMessage() {
		cout << m_a << endl;
		cout << m_b << endl;
		cout << m_c << endl;

	}
};

int main() {
	// 1.通过构造函数列表初始化  类中的属性,
	Person p(40,50,60);
	//p.GetMessage(10,20,30);
	p.printfMessage();



	system("pause");
	return 0;
}

类对象作为类成员

?? ?// 当类中成员是其他类对象是 ?称该成员为对象成员
?? ?// 构造的顺序是:先调用对象成员的构造,且最后才析构

class Person {
public:
?? ?// m_Pname(Pname)相当于 实例化了手机类并传递手机名字过去
??
?//m_Pname(Pname) ?Phone m_Pname(name)隐士声明----- 调用手机构造函数
?? ?Person(string Mname, string Pname) :m_Name(Mname), m_Pname(Pname) {
?? ??? ?cout << "这是人的构造函数" << endl;
?? ?}
?? ?~Person() {
?? ??? ?cout << "这是人的析构函数" << endl;
?? ?}
?? ?string m_Name;
?? ?Phone m_Pname;

};

#include <iostream>
#include "string.h"
using namespace std;

class Phone {
public:
	Phone(string Pname) :p_name(Pname) {
		cout << "这是手机的构造函数" << endl;
	}

	~Phone() {
		cout << "这是手机的析构函数" << endl;
	}
	string p_name;
};

class Person {
public:
	// m_Pname(Pname)相当于 实例化了手机类并传递手机名字过去
	//m_Pname(Pname)  Phone m_Pname(name)隐士声明----- 调用手机构造函数
	Person(string Mname, string Pname) :m_Name(Mname), m_Pname(Pname) {
		cout << "这是人的构造函数" << endl;
	}
	~Person() {
		cout << "这是人的析构函数" << endl;
	}
	string m_Name;
	Phone m_Pname;

};

void demo01() {
	// 当类中成员是其他类对象是  称该成员为对象成员
	// 构造的顺序是:先调用对象成员的构造,且最后才析构
	Person p1("张三", "爱疯x");
	cout << p1.m_Name << "的手机是:" << p1.m_Pname.p_name << endl;
}

int main() {
	demo01();

	system("pause");
	return 0;
}

静态成员(属性、方法)

静态成员就是在成员变量和成员函数前加上关键字 static ,称为静态成员
静态成员分为:
静态成员变量:
所有对象共享同一份数据
在编译阶段分配内存
类内声明,类外初始化
静态成员函数:
所有对象共享同一个函数
静态成员函数只能访问静态成员变量
#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:

	Person() {
		cout << "这是构造函数" << endl;
	}
	static int m_Age;

	//私有属性的静态成员也是有权限的
private:
	static int m_Agf;
};

// 2.1 类内声明 类外初始化
int Person::m_Age = 100; 
int Person::m_Agf = 200;

void demo01() {
	Person p;
	cout << p.m_Age << endl;

	Person p1;
	p1.m_Age = 200;
	cout << p.m_Age << endl;
}

void demo02() {
	//两种调用方法
	//方法1
	Person p;
	cout << p.m_Age << endl;
	//方法2
	cout << Person::m_Age << endl;
}

void demo03() {
	//私有属性的静态成员也是有权限的
	cout << Person::m_Age << endl;
	//cout << Person::m_Agf << endl; 错误 不能访问
}

int main() {
	// 1静态成员在编译阶段就已经分配内存
	// 2静态成员必须在类内声明 类外初始化 例如 int Person:: m_age = 100;
	// 3类内的所有对象共享同一份内存
	//demo01();

	//两种调用方法 因为该静态成员不属于其中任何一个类
	//demo02();

	//私有属性的静态成员也是有权限的
	demo03();


	system("pause");
	return 0;
}

?静态函数(调用2、特点、权限)

#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:

	static void message() {
		cout << "这是静态成员函数" << endl;
		cout << m_name << endl;				//访问静态变量 可以
	//	cout << m_Age << endl;				//错误 因为不能访问非静态变量
	}
	static string m_name;
	int m_Age;

private:
	static void address() {
		cout << "这是私有权限的静态函数" << endl;
	}
};
string Person::m_name = "张三";


void demo01() {
	// 两种调用方式
	Person p1;
	p1.message();

	Person::message();
}

void demo02() {
	// 静态函数只能访问静态属性
	// 静态函数不能访问非静态变量,因为不知道非静态变量属于谁,而静态变量是谁都不属于
	Person p1;
	p1.message();

	Person::message();
}

void demo03() {
	// 私有权限的静态函数
	//Person::address();	//不能访问
}

int main() {
	// 1静态成员在编译阶段就已经分配内存
	// 3类内的所有对象共享同一份内存

	//两种调用方式
	//demo01();

	//静态函数只能访问静态变量
	demo02();

	//静态函数也有权限限制
	//demo03();

	system("pause");
	return 0;
}

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

C++ 中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
#include <iostream>
#include "string.h"
using namespace std;

class Person {
	int age;		//非静态变量	占用对象空间
	static int agr; //静态成员变量  不占用对象空间
	void agt() {	//非静态成员函数不占用对象空间
	}
	static void agy() {//静态成员函数不占用对象空间
	}
};

void demo01() {
	Person p1;
	cout << "Person类的大小是:" << sizeof(p1) << endl;
}

int main() {
	// 1.成员变量和成员函数分开存储

	// 2.只有非静态成员变量才占用对象空间 ---- 因为其它存储在别的区域

	// 3.空的类占用1个字节
	demo01();

	system("pause");
	return 0;
}

?this关键字用法

c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象

this 指针是隐含每一个非静态成员函数内的一种指针
this 指针不需要定义,直接使用即可
this 指针的用途:
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,可使用return *this

?? ?Person& PersonAddAge(Person p) {?? ?//返回这个类的引用。 ?如果没有引用 相当于有新拷贝了一个
?? ??? ?this->age += p.age;
?? ??? ?return *this;?? ??? ??? ??? ??? ?//返回这个类的值
?? ?}?

#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:
	Person(int age){
		cout << "这是默认构造函数" << endl;
		this->age = age;
	}
	int age;

	Person& PersonAddAge(Person p) {	//返回这个类的引用。  如果没有引用 相当于有新拷贝了一个
		this->age += p.age;
		return *this;					//返回这个类的值
	}
};

//void demo01() {
//	Person p1(18);//成员函数--构造函数
//	cout << p1.age << endl;
//}

//void demo02() {
//	Person p1(18);
//	p1.PersonAddAge(p1);
//	cout << p1.age << endl;
//}

void demo03() {
	Person p1(18);
	p1.PersonAddAge(p1).PersonAddAge(p1);
	cout << p1.age << endl;
}

int main() {
	// 1.this指针 指向调用成员函数所属的对象

	// 2.当形参和成员函数同名时 可以用this来区分
	//demo01();

	// 3.在类的非静态成员函数中返回对象本身,可以用返回值引用和 return *this
	demo03();

	system("pause");
	return 0;
}

?空指针访问成员函数 和this冲突

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

#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:
	void demo01() {
		cout << "这是空指针无操作" << endl;
	}
	void demo02() {
		cout << "这是空指针this" << endl;
		if (this == NULL) {
			return;
		}
		cout << "age = " << this->age << endl;

	}
	int age;
};

void demo() {
	Person* p = NULL;//初始化创建这个类的指针为空
	p->demo01();
	p->demo02();
}
int main() {
	// 1.空指针也能访问类中成员
	demo();

	// 2.空指针访问成员函数时如果this冲突需要加上条件

	system("pause");
	return 0;
}

const修饰成员函数

常函数:(修饰的是this 不能通过常函数改变值 如果要改则需要加上mutable)
/this 指针的本质是一个指针常量,指针的指向不可修改? const Person* this
成员函数后加 const 后我们称为这个函数为 常函数
常函数内不可以修改成员属性
成员属性声明时加关键字 mutable 后,在常函数中依然可以修改
常对象:
声明对象前加 const 称该对象为常对象
常对象只能调用常函数
#include <iostream>
#include "string.h"
using namespace std;

class Person {
public:
	void showPerson()const {//修饰的是this
		//this->m_A = 100;	//加上const是常函数 不能修改值
		this->m_B = 200;	//想要修改前面必须加上mutalble
	}

	void demo() {
		m_B = 300;
	}

	int m_A;
	mutable int m_B;
};

void demo01() {
	// 1.1常函数相当于在函数前面加上了const   作业是不能修改值,但是可以修改方向
	// 1.2常函数修饰的是this
	// 1.3如果任然想修改值需要在属性前面加上 mutable关键字
	Person p;
	p.showPerson();
}

void demo02() {
	// 2.1常对象
	const Person p2;
	//p2.m_A = 100; //错误 常对象也不能修改属性
	p2.m_B = 200;
	
	//p2.demo();	//错误 常对象只能调用常函数。来修改
	
}
int main() {
	// 1.常函数
	demo01();

	// 2.常对象
	demo02();

	system("pause");
	return 0;
}

友元

生活中你的家有客厅 (Public) ,有你的卧室 (Private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去
但是呢,你也可以允许你的好闺蜜好基友进去。
在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的关键字为 ==friend==
友元的三种实现
全局函数做友元 、类做友元 、成员函数做友元
#include <iostream>
#include "string.h"
using namespace std;

class Building {
	//声明友元全局函数
	friend void QuanJu(Building* building);

public:
	Building() {
		KeTing = "这里是客厅";
		WoShi = "这里是卧室";
	}
public:
	string KeTing;
private:
	string WoShi;
};

//全局函数
void QuanJu(Building* building) {
	cout << "全局函数正在访问客厅:。。。。" << building->KeTing << endl;
	cout << "全局函数正在访问客厅:。。。。" << building->WoShi << endl;
}

void demo01() {
	Building building;//初始化 并调用构造函数赋值
	QuanJu(&building);
}

int main() {

	// 友元的三种三种方法之一---- 全局函数
	// 通过全局函数访问私有属性
	demo01();

	system("pause");
	return 0;
}

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