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

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赋值给acout << "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;		//输出60cout << 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 引用是可读可写的所以传入ademo01(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;//通过传入参数赋值给namevoid 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://设置xvoid set_m_x(int x) {m_x = x;}//获取xint get_m_x() {return m_x;}//设置yvoid set_m_y(int y) {m_y = y;}//获取xint 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() {//两种调用方法//方法1Person p;cout << p.m_Age << endl;//方法2cout << 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 *thisdemo03();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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/414648.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

15、注册表永久关闭Citrix云桌面UAC功能

一、说明 我们经常使用Citrix云桌面时候遇到打开软件会弹出一个输密码的界面&#xff0c;这个其实是UAC&#xff0c;此次我们主要使用注册表方式彻底关闭。 二、注册表方式 打开路径&#xff1a;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Syst…

新版AndroidStudio dependencyResolutionManagement出错

在新版AndroidStudio中想像使用4.2版本或者4.3版本的AndroidStudio来构造项目&#xff1f;那下面这些坑我们就需要来避免了&#xff0c;否则会出各种各样的问题。 一.我们先来看看新旧两个版本的不同。 1.jdk版本的不同 新版默认是jdk17 旧版默认是jdk8 所以在新版AndroidSt…

Mysql root 密码重置详解

文章目录 1 概述1.1 前言1.2 mysql 版本查询 2 windows 操作系统2.1 mysql 8 及以上版本2.1.1 关闭 mysql 服务2.1.2 通过无认证方式启动 mysql2.1.3 新开窗口&#xff0c;登录 mysql&#xff0c;重置密码 1 概述 1.1 前言 不同的操作系统&#xff08;如&#xff1a;windows、…

Unity 编辑器篇|(十)Handles (全面总结 | 建议收藏)

目录 1. 前言2 参数总览3 Handles两种使用方式3.1 基于Editor类的OnSceneGUI3.2 基于EditorWindow 4 Handles绘制4.1 Draw&#xff1a;绘制元几何体(点、线、面)4.1.1 抗锯齿&#xff1a; DrawAAPolyLine 、 DrawAAConvexPolygon4.1.2 绘制实线: DrawLine 、 DrawLines 、DrawP…

Python基础第一篇(Python概念介绍)

文章目录 一、前言&#xff1a;二、第一个Python程序三&#xff0c;理解Python的解释器四&#xff0c;Python解释器的使用五&#xff0c;Python开发环境 一、前言&#xff1a; 欢迎来到我们的Python学习专栏。在这里&#xff0c;我们将一起探索Python这门强大、灵活、易于学习…

electron+vite+vue3 快速入门教程

文章目录 前言一、electron是什么&#xff1f;二、electron 进程模型1.主进程2.渲染进程3.预加载脚本4.进程通信4.1 sendon&#xff08;单向&#xff09;4.2 invokehandle (双向)4.3 主进程向渲染进程发送事件 三、窗口创建与应用事件四、技术栈和构建工具五、electron-vite安装…

龙年-微信定制红包封面,送一波

龙年新年马上就要来临了&#xff0c;我定制两款红包封面&#xff0c;送给大家。这次是借助AI的能力&#xff0c;自己独立完成&#xff0c;这大概就是这波AI浪潮的魅力&#xff0c;人人皆可参与。 另外&#xff0c;微信平台也给我推送了自己的公众号创作回顾&#xff0c;没想到居…

Unity常用的优化技巧集锦

Unity性能优化是面试的时候经常被问道的一些内容&#xff0c;今天给大家分享一些常用的Unity的优化技巧和思路&#xff0c;方便大家遇到问题时候参考与学习。 对啦&#xff01;这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白&#xff0c;也有一些正在从事游…

RabbitMQ 部署与配置[CentOS7]

# RabbitMQ,Erlang 版本包对应 https://rabbitmq.com/which-erlang.html#eol-seriescd /usr/local/src# Erlang下载 # https://github.com/rabbitmq/erlang-rpm/releases https://github.com/rabbitmq/erlang-rpm/releases/download/v23.3.4.5/erlang-23.3.4.5-1.el7.x86_64.rp…

FFmpeg之AVFilter

文章目录 一、概述二、重要结构体2.1、AVFilterGraph2.2、AVFilter2.3、AVFilterContext 三、流程梳理3.1、FFmpeg AVFilter 使用整体流程3.2、过滤器构建流程3.2.1、分配AVFilterGraph3.2.2、创建过滤器源3.2.3、创建接收过滤器3.2.4、生成源和接收过滤器的输入输出3.2.5、通过…

虚拟线程探索与实践(JDK19)

优质博文&#xff1a;IT-BLOG-CN 一、简介 虚拟线程是轻量级线程&#xff0c;极大地减少了编写、维护和观察高吞吐量并发应用的工作量。虚拟线程是由JEP 425提出的预览功能&#xff0c;并在JDK 19中发布&#xff0c;JDK 21中最终确定虚拟线程&#xff0c;以下是根据开发者反馈…

4、Redis高并发分布式锁实战

引言 在分布式系统中&#xff0c;保证数据的一致性和避免竞争条件是至关重要的。分布式锁是一种常用的机制&#xff0c;而Redis作为一款高性能的内存数据库&#xff0c;提供了简单而强大的分布式锁方案。本文将深入探讨如何利用Redis高并发分布式锁来解决分布式系统中的并发控…