内存分区模型
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;
}
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. 构造函数,没有返回值也不写 void2. 函数名称与类名相同3. 构造函数可以有参数,因此可以发生重载4. 程序在调用对象时候会自动调用构造,无须手动调用 , 而且只会调用一次析构函数语法: ~ 类名 (){}1. 析构函数,没有返回值也不写 void2. 函数名称与类名相同 , 在名称前加上符号 ~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;
}
#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;
}
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;
}
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;
}
常函数:(修饰的是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;
}