目录

  • Chapter 0 | C与C++
  • Chapter 1 | 程序的内存模型
    • 1.1 程序运行前
    • 1.2 程序运行后
    • 1.3 new操作符
  • Chapter 2 | 引用
    • 2.1 引用的基本使用
    • 2.2 引用注意事项
    • 2.3 引用做函数参数
    • 2.4 引用做函数返回值
    • 2.5 引用的本质
    • 2.6 常量引用
  • Chapter 3 | 函数提高
    • 3.1 函数默认参数
    • 3.2 函数占位参数
    • 3.3 函数重载
      • 3.3.1 函数重载概述
      • 3.3.2 函数重载注意事项
  • Chapter 4 | 类和对象
    • 4.1 封装
      • 4.1.1 封装的意义
      • 4.1.2 struct和class的区别
      • 4.1.3 成员属性设置为私有
      • 4.1.4 练习案例
    • 4.2 对象的初始化和清理
      • 4.2.1 构造函数和析构函数
      • 4.2.2 构造函数的分类及调用
      • 4.2.3 拷贝构造函数调用时机
      • 4.2.4 构造函数调用规则
      • 4.2.5 深拷贝和浅拷贝
      • 4.2.6 初始化列表
      • 4.2.7 类对象作为类成员
      • 4.2.8 静态成员
    • 4.3 C++对象模型和this指针
    • 4.4 友元
    • 4.5 运算符重载
    • 4.6 继承
    • 4.7 多态

Chapter 0 | C与C++

基础部分C和C++的区别:

  • 头文件 #include < iostream >
  • 标准命名空间using namespace std
  • 输入 cin >> 输出 cout << << endl
  • system(“pause”); 按任意键结束
  • string字符串,要带< string >头文件。

Chapter 1 | 程序的内存模型

内存四区

C++程序在执行时,将内存大方向划分为四个区域。

  • 代码区:存放函数体的二进制代码,由操作系统进行管理。
  • 全局区:存放全局变量和静态变量以及常量。
  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

内存四区的意义

不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。


1.1 程序运行前

在程序编译后,生成了可执行.exe可执行程序,未执行该程序前分为两个区域。

代码区

  • 存放CPU执行的机器指令。
  • 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。
  • 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令。

全局区

  • 全局变量和静态变量存放在此。
  • 全局区还包含了常量区,字符串常量和其他常量也存放在此。
  • 该区域的数据在程序结束后由操作系统释放。

总结

  • C++中在程序运行前分为全局区和代码区
  • 代码区的特点是共享和只读
  • 全局区中存放全局变量、静态变量(static关键字)、常量

判断是否在全局区:

1.2 程序运行后

栈区

  • 由编译器自动分配释放,存放函数的形参,局部变量等。
  • 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放。

注意事项解析:

堆区

  • 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。
  • 在C++中主要利用new在堆区开辟内存。

在堆区开辟数据

总结

  • 堆区数据由程序员管理和释放。
  • 堆区数据利用new关键字进行开辟内存。

1.3 new操作符

程序员手动开辟数据和释放

C++中利用new操作符在堆区开辟数据。释放利用操作符delete
利用new创建的数据,会返回该数据对应的类型的指针。

语法

new 数据类型

示例1 - 基本语法

#include <iostream>
using namespace std;int* test1()
{int* a = new int(10);return a;
}
int main()
{int* p = test1();cout << *p << endl;//10cout << *p << endl;//10delete p;//cout << *p << endl;//报错,释放的空间不可访问。system("pause");return 0;
}

示例2 - 开辟数组

int main()
{//堆区开辟数组int* arr = new int[10];for (int i = 0; i < 10; i++){arr[i] = i + 100;}//打印for (int i = 0; i < 10; i++){cout << arr[i] << endl;}delete[] arr;//释放数组 delete后加[]system("pause");return 0;
}

Chapter 2 | 引用

2.1 引用的基本使用

作用

给变量起别名。

语法

数据类型 &别名 = 原名

示例

#include<iostream>
using namespace std;int main()
{int a = 10;int& b = a;cout << "a = " << a << endl;cout << "b = " << b << endl;b = 100;cout << "--------改变后--------" << endl;cout << "a = " << a << endl;cout << "b = " << b << endl;system("pause");return 0;
}

2.2 引用注意事项

  • 引用必须初始化
  • 引用在初始化后,不可以改变

示例

int main()
{int a = 10;//int &b;//错误的,引用必须初始化int& b = a;int c = 20;//一旦初始化后就不可以更改b = c;//这是赋值操作,不是更改引用cout << "a = " << a << endl;//20cout << "b = " << b << endl;//20cout << "c = " << c << endl;//20system("pause");return 0;
}

2.3 引用做函数参数

作用

函数传参时,可以利用技术让形参修饰实参

优点

可以简化指针修改实参

示例

//引用传递
void my_swap(int& a, int& b)
{int tmp = b;b = a;a = tmp;
}
int main()
{int a = 10;int b = 20;my_swap(a, b);cout << "a = " << a << endl;//20cout << "b = " << b << endl;//10system("pause");return 0;
}

通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单。

2.4 引用做函数返回值

作用

引用是可以作为函数的返回值存在的

注意事项

不要返回局部变量引用

用法

函数调用作为左值

示例

int& test1()
{int a = 10;return a;
}int& test2()
{static int a = 10;return a;
}void test3()
{;
}
int main()
{//不要返回局部变量引用int& ref1 = test1();cout << "ref1 = " << ref1 << endl;//第一次正确是因为编译器做了保留cout << "ref1 = " << ref1 << endl;//第二次错误是因为a的内存已经释放//返回静态变量引用int& ref2 = test2();cout << "ref2 = " << ref2 << endl;//10cout << "ref2 = " << ref2 << endl;//10//如果函数做左值,那么必须返回引用test2() = 1000;cout << "ref2 = " << ref2 << endl;//1000cout << "ref2 = " << ref2 << endl;//1000//test3() = 10;//错误的,表达式必须是可修改的左值system("pause");return 0;
}
}

2.5 引用的本质

本质

引用的本质在C++内部实现是一个指针常量

示例

void func(int& ref)
{ref = 100;//ref是引用,自动转换为 *ref = 100
}
int main()
{int a = 10;int& ref = a;//自动转换为 int* const ref = &a; //指针常量是指针指向不可改,也说明为什么引用不可更改ref = 20;//内部发现ref是引用,自动转换为 *ref = 20cout << "a = " << a << endl;//20cout << "ref = " << ref << endl;//20func(a);cout << "a = " << a << endl;//100cout << "ref = " << ref << endl;//100system("pause");return 0;
}

C++推荐使用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器帮我们做了。

2.6 常量引用

作用

常量引用主要用来修饰形参,防止误操作。

示例

在函数形参列表中,可以加const修饰形参,防止形参改变实参。

void show_value(const int& a)
{//a = 1000;//不可修改cout << "a = " << a << endl;//100
}
int main()
{//int& ref = 10;//引用本身需要一个合法的内存空间,因此运行错误const int& ref = 10;//加上const就可以//编译器优化代码:int tmp = 10; const int& ref = tmp;//ref = 100;//加入const后不可以修改变量cout << "ref = " << ref << endl;//10//函数中利用常量引用防止误操作修改实参int a = 100;show_value(a);system("pause");return 0;
}

Chapter 3 | 函数提高

3.1 函数默认参数

在C++中,函数的形参列表中的形参是可以有默认值的。

语法

返回值类型 函数名 (参数 = 默认值)  {}

示例

#include <iostream>
using namespace std;//注意事项1:如果某个位置有了默认参数,那么从这个位置往后,就必须有默认值
//int add (int a = 10, int b, int c)//error
int add(int a, int b = 10, int c = 10)
{return a + b + c;
}//注意事项2:函数声明和实现只能一个有默认参数
int test(int a = 10, int b = 10);//声明
//int test(int a = 10, int b = 10){}//errorint test(int a, int b)//实现
{//test
}int main()
{//函数默认参数//如果我们自己传入数据,就用自己的数据,如果没有就用默认值cout << add(10) << endl;system("pause");return 0;
}

3.2 函数占位参数

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

语法

返回值类型 函数名 (数据类型) {}

示例

//目前阶段占位参数还用不到,后面会用到
void func1(int a, int)//占位参数
{cout << "func1" << endl;
}
void func2(int a, int = 10)//占位参数也可以有默认参数
{cout << "func2" << endl;
}
int main()
{func1(10, 10);//占位参数无默认参数时必须填补func2(10, 20);//有默认参数时也可以传system("pause");return 0;
}

3.3 函数重载

3.3.1 函数重载概述

作用

函数名可以相同,提高复用性。

函数重载满足条件

  • 同一个作用域下
  • 函数名称相同
  • 函数参数 类型不同 或者 个数不同 或者 顺序不同

注意

函数的返回值不可以作为函数重载的条件

示例

//函数重载需要函数都在一个作用域下//个数不同
void func()
{cout << "func()被调用" << endl;
}
void func(int a)
{cout << "func(int a)被调用" << endl;
}//类型不同
void func(double a)
{cout << "func(double a)被调用" << endl;
}//顺序不同
void func(int a, double b)
{cout << "func(int a, double b)被调用" << endl;
}
void func(double a, int b)
{cout << "func(double a, int b)被调用" << endl;
}//函数返回值不可以作为函数重载条件
/*
int func(double a, int b)
{cout << "func(double a, int b)被调用" << endl;
}
*/int main()
{func();func(100);func(3.0);func(100, 3.0);func(3.0, 100);system("pause");return 0;
}

3.3.2 函数重载注意事项

  • 引用作为重载条件
  • 函数重载碰到函数默认参数

示例

//1.引用作为重载条件
void func1(int& a)// int& a = 10 不合法
{cout << "func(int& a)被调用" << endl;
}
void func1(const int& a)// const int& a = 10 合法
{cout << "func(const int& a)被调用" << endl;
}//2.函数重载碰到默认参数void func2(int a)
{cout << "func(int a)被调用" << endl;
}
void func2(int a, int b = 10)
{cout << "func(int a, int b = 10)被调用" << endl;
}
int main()
{//1.int num = 10;func1(num);//调用无constfunc1(10);//调用有const//2.//func2(10);//碰到默认参数出现二义性,会报错,要避免这种情况func2(10, 20);//"func(int a, int b = 10)被调用"system("pause");return 0;
}

Chapter 4 | 类和对象

C++面向对象的三大特性为:封装、继承、多态

C++认为万事万物皆为对象,对象上有其属性和行为

例如:

人可以作为对象;属性有 姓名、年龄、身高...;行为有 走、跑、跳...
车可以作为对象;属性有 轮胎、车灯、方向盘...;行为有 载人、放音乐、放空调...

具有相同性质的对象,我们可以抽象为类,人属于人类,车属于车类。

4.1 封装

封装是C++面向对象三大特性之一

4.1.1 封装的意义

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制

封装意义1

在设计类的时候,属性和行为写在一起,表现事物

语法

class 类名 { 访问权限 : 属性 / 行为 };

示例1:设计一个圆类,求圆的周长

#include <iostream>
using namespace std;const double PI = 3.14;class Circle
{//访问权限//公共权限
public://属性//半径int m_r;//member_r//行为//获取圆的周长double calculateC(){return 2 * PI * m_r;}
};
int main()
{//通过圆类,创建具体的圆(对象)//实例化 - 通过一个类创建一个对象的过程Circle cl;//给圆对象的属性进行赋值cl.m_r = 10;//2 * PI * 3.14 = 62.8cout << "圆的周长为:" << cl.calculateC() << endl;system("pause");return 0;
}

示例2:设计学生类

class Student
{//访问权限
public://属性string m_name;string m_id;//行为void print_information(){cout << "名字是:" << m_name << "\n" << "学号是:" << m_id << endl;}void set_name(string name){m_name = name;}void set_id(string id){m_id = id;}
};
int main()
{Student s;s.set_name("Luckyhorse5");s.set_id("20230404");s.print_information();system("pause");return 0;
}

类中的属性和行为统一称为 成员
属性 - 成员属性/成员变量
行为 - 成员函数/成员方法

封装意义2

类在设计时,可以把属性和行为放在不同的权限下,加以控制

访问权限有三种:

访问权限 范围
public 公共权限 类内可以访问 类外可以访问
protected 保护权限 类内可以访问 类外不可以访问 儿子可以访问父亲的保护内容
private 私有权限 类内可以访问 类外不可以访问 儿子不可以访问父亲的私有内容

示例

class Person
{public:string m_name;protected:string m_car;private:int m_password;public:void func(){m_name = "小赵";m_car = "Camry";m_password = 2023;}
};
int main()
{Person p;p.m_name = "小张";//p.m_car = "Corolla";//保护权限内容,类外不可访问//p.m_password = 123;//私有权限内容,类外不可访问p.func();system("pause");return 0;
}

4.1.2 struct和class的区别

在C++中,struct和class唯一的区别在于默认访问权限不同

区别

  • struct默认权限为公共
  • class默认权限为私有

示例

class C
{int m_c;//默认是私有权限
};
struct S
{int m_s;//默认是公共权限
};
int main()
{C c;S s;//c.m_c = 10;//私有权限类外不可访问s.m_s = 10;//可以访问system("pause");return 0;
}

4.1.3 成员属性设置为私有

优点

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

示例

class Person
{//将所有成员属性设置为私有,可以自己控制读写权限
private://姓名 可读可写string m_name;//年龄 只读 如果写需要在0-120之间int m_age = 0;//情人 只写string m_lover;public:string get_name()//读姓名{return m_name;}void set_name(string name)//写姓名{m_name = name;}int get_age()//读年龄{return m_age;}//对于写权限,我们可以检测数据的有效性void set_age(int age)//在一定范围内写年龄{if (age < 0 || age > 120){cout << "年龄输入有误!" << endl;return;}m_age = age;}void set_lover(string lover)//写情人{m_lover = lover;}
};
int main()
{Person p;p.set_name("Luckyhorse5");cout << "姓名是:" << p.get_name() << endl;p.set_age(19);cout << "年龄是:" << p.get_age() << endl;p.set_lover("W");system("pause");return 0;
}

4.1.4 练习案例

设计案例1:立方体类

设计立方体类(Cube)
求出立方体的面积和体积
分别用全局函数和成员函数判断两个立方体是否相等

class Cube
{private:int m_length;//长int m_width;//宽int m_height;//高public:void set_length(int length)//设置长{m_length = length;}int get_length()//获取长{return m_length;}void set_width(int width)//设置宽{m_width = width;}int get_width()//获取宽{return m_width;}void set_height(int height)//设置高{m_height = height;}int get_height()//获取高{return m_height;}int get_area()//获取面积{return m_length * m_width + m_length * m_height + m_height * m_width;}int get_vol()//获取体积{return m_length * m_width * m_height;}//成员函数判断两个立方体是否相等bool is_same_by_class(Cube& c2){if (m_length == c2.get_length() && m_width == c2.get_width() && m_height == c2.get_height()){return true;}elsereturn false;}
};//全局函数判断两个立方体是否相等
bool is_same_by_global(Cube &c1, Cube &c2)
{if (c1.get_length() == c2.get_length() && c1.get_width() == c2.get_width() && c1.get_height() == c2.get_height()){return true;}elsereturn false;
}
int main()
{Cube c1;c1.set_length(5);c1.set_width(4);c1.set_height(3);cout << "c1立方体的面积是:" << c1.get_area() << endl;c1.get_vol();cout << "c1立方体的体积是:" << c1.get_vol() << endl;Cube c2;c2.set_length(5);c2.set_width(4);c2.set_height(3);bool ret_g = is_same_by_global(c1, c2);if (ret_g == true)cout << "两个立方体相等" << endl;elsecout << "两个立方体不相等" << endl;bool ret_c = c1.is_same_by_class(c2);if (ret_c == true)cout << "两个立方体相等" << endl;elsecout << "两个立方体不相等" << endl;system("pause");return 0;
}

设计案例2:点和圆关系

设计一个圆类(Circle)和一个点类(Point),计算点和圆的关系

class Point
{private:int m_x;//点的x坐标int m_y;//点的y坐标
public:void set_x(int x)//设置x{m_x = x;}int get_x()//获取x{return m_x;}void set_y(int y)//设置y{m_y = y;}int get_y()//获取y{return m_y;}
};class Circle
{private:int m_r;//圆的半径//在类中可以让另一个类作为本类中的成员Point m_center;//圆的圆心(Point类型)
public:void set_r(int r)//设置半径{m_r = r;}int get_r()//获取半径{return m_r;}void set_center(Point center)//设置圆心{m_center = center;}Point get_center()//获取圆心{return m_center;}
};void relation_circle_and_point(Circle& c, Point& p)
{//计算点和圆心距离double distance = sqrt(pow((c.get_center().get_x() - p.get_x()), 2) +pow((c.get_center().get_y() - p.get_y()), 2));//判断距离和半径的关系if (distance == 0)cout << "点在圆心" << endl;else if (distance > 0 && distance < c.get_r())cout << "点在圆内" << endl;else if (distance == c.get_r())cout << "点在圆上" << endl;elsecout << "点在圆外" << endl;
}
int main()
{//创建圆Circle c;Point center;center.set_x(0);center.set_y(0);c.set_center(center);c.set_r(2);//创建点Point p;p.set_x(2);p.set_y(0);//判断点和圆的关系relation_circle_and_point(c, p);system("pause");return 0;
}

将设计案例2拆分成到不同文件

circle.h头文件

#pragma once
#include "point.h"class Circle
{private:int m_r;//圆的半径//在类中可以让另一个类作为本类中的成员Point m_center;//圆的圆心(Point类型)
public:void set_r(int r);//设置半径int get_r();//获取半径void set_center(Point center);//设置圆心Point get_center();//获取圆心
};

point.h头文件

#pragma onceclass Point
{private:int m_x;//点的x坐标int m_y;//点的y坐标
public:void set_x(int x);//设置xint get_x();//获取xvoid set_y(int y);//设置yint get_y();//获取y
};

circle.cpp源文件

#include "circle.h"void Circle::set_r(int r)//设置半径
{m_r = r;
}
int Circle::get_r()//获取半径
{return m_r;
}
void Circle::set_center(Point center)//设置圆心
{m_center = center;
}
Point Circle::get_center()//获取圆心
{return m_center;
}

point.cpp源文件

#include "point.h"void Point::set_x(int x)//设置x
{m_x = x;
}
int Point::get_x()//获取x
{return m_x;
}
void Point::set_y(int y)//设置y
{m_y = y;
}
int Point::get_y()//获取y
{return m_y;
}

test.cpp源文件

#include <iostream>
#include "circle.h"
//#include "point.h"//只需要包含circle.h即可,因为circle.h包含了point.husing namespace std;void relation_circle_and_point(Circle& c, Point& p)
{//计算点和圆心距离double distance = sqrt(pow((c.get_center().get_x() - p.get_x()), 2) +pow((c.get_center().get_y() - p.get_y()), 2));//判断距离和半径的关系if (distance == 0)cout << "点在圆心" << endl;else if (distance > 0 && distance < c.get_r())cout << "点在圆内" << endl;else if (distance == c.get_r())cout << "点在圆上" << endl;elsecout << "点在圆外" << endl;
}
int main()
{//创建圆Circle c;Point center;center.set_x(0);center.set_y(0);c.set_center(center);c.set_r(2);//创建点Point p;p.set_x(2);p.set_y(0);//判断点和圆的关系relation_circle_and_point(c, p);system("pause");return 0;
}

4.2 对象的初始化和清理

  • 生活中我们买的电子产品基本都有出厂设置,某一天我们不用的时候也会删除一些自己信息数据保证安全
  • C++中的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前清理数据的设置

4.2.1 构造函数和析构函数

对象的初始化和清理是两个非常重要的安全问题:
一个对象或者变量没有初始状态,对其使用后果是未知。同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题。

C++中利用了构造函数析构函数解决上述问题,这两个函数会被编译器自动调用,完成对象的初始化和清理工作。对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供
编译器提供的构造函数和析构函数是空实现。

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

构造函数语法

类名 (){}
  • 构造函数,没有返回值也不写void
  • 函数名称与类名相同
  • 构造函数可以有参数,因此可以发生重载
  • 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数语法

~类名 (){}
  • 析构函数,没有返回值也不写void
  • 函数名称与类名相同,在名称前加上符号 ~
  • 析构函数不可以有参数,因此不可以发生重载
  • 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

示例

#include<iostream>
using namespace std;class Person
{public:Person(){cout << "构造函数的调用" << endl;}~Person(){cout << "析构函数的调用" << endl;}
};
void test()
{Person p1;//在栈上的数据,test执行完毕后,释放这个对象
}
int main()
{test();Person p2;//main还在执行,按完任意键之后才调用析构system("pause");return 0;
}

4.2.2 构造函数的分类及调用

两种分类方式:

  • 按参数分为:有参构造 和 无参构造(默认构造)
  • 按类型分为:普通构造 和 拷贝构造

三种调用方式:

  • 括号法
  • 显示法
  • 隐式转换法

示例

//分类
class Person
{public:int m_age = 0;
public://普通构造函数Person()//无参{cout << "Person的无参构造函数调用" << endl;}Person(int a)//有参{m_age = a;cout << "Person的有参构造函数调用" << endl;}//拷贝构造函数Person(const Person& p){//将传入的人身上的所有属性拷贝到我身上m_age = p.m_age;cout << "Person的拷贝构造函数调用" << endl;}~Person(){cout << "Person的析构函数调用" << endl;}
};
//调用
void test()
{//1.括号法cout << "------------1------------" << endl;Person p11;//默认构造函数的调用//注意事项1:调用默认构造函数的时候,不要加()//Person p14();//编译器会认为是一个函数的声明,不会认为在创建对象Person p12(10);//有参构造函数的调用Person p13(p12);//拷贝构造函数的调用cout << "p12的年龄为:" << p12.m_age <<endl;//10cout << "p13的年龄为:" << p13.m_age << endl;//10//2.显示法cout << "------------2------------" << endl;Person p21;Person p22 = Person(20);//有参构造//Person(20);//等式右边单独拿出来是 匿名对象 //匿名对象特点:当前行执行结束后,系统会立即回收掉匿名对象//cout << "aaaaaaaaaaa" << endl;//在调用析构函数后才打印Person p23 = Person(p22);//拷贝构造//注意事项2:不要利用拷贝构造函数,来初始化匿名对象//Person(p23);//编译器会认为 Person(p23) === Person p23,报错重定义cout << "p22的年龄为:" << p22.m_age << endl;//20cout << "p23的年龄为:" << p23.m_age << endl;//20//3.隐式转换法cout << "------------3------------" << endl;Person p32 = 30;//相当于转换成显示法 Person p31 = person(10);有参构造Person p33 = p32;//拷贝构造
}
int main()
{test();system("pause");return 0;
}

4.2.3 拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象

示例

class Person
{public:int m_age = 0;
public:Person(){cout << "Person默认构造函数被调用" << endl;}Person(int age){m_age = age;cout << "Person有参构造函数被调用" << endl;}Person(const Person& p){m_age = p.m_age;cout << "Person拷贝构造函数被调用" << endl;}~Person(){cout << "Person析构函数被调用" << endl;}
};//1.使用一个已经创建完毕的对象来初始化一个新对象
void test1()
{Person p11(10);Person p12(p11);cout << "p12的年龄为" << p12.m_age << endl;
}//2.值传递的方式给函数参数传值
void dowork1(Person p)
{cout << p.m_age << endl;
}
void test2()
{Person p21(20);//有参构造dowork1(p21);//拷贝构造
}//3.以值方式返回局部对象
Person dowork2()
{Person p;//1.默认构造cout << &p << endl;return p;//2.return p的时候拷贝给test3()中的p31
}//3.dowork2执行结束后 析构函数被调用(析构的是p)
void test3()
{Person p31 = dowork2();cout << &p31 << endl;
}//4.test3执行结束后 析构函数被调用(析构的是p31)
int main()
{cout << "---------------1---------------" << endl;test1();cout << "---------------2---------------" << endl;test2();cout << "---------------3---------------" << endl;test3();system("pause");return 0;
}

4.2.4 构造函数调用规则

默认情况下,C++编译器至少给一个类添加3个函数

  1. 默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则

  • 如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,C++不再提供其他构造函数

4.2.5 深拷贝和浅拷贝

深浅拷贝是面试经典问题,也是常见的一个坑。

浅拷贝:简单的赋值拷贝操作。

深拷贝:在堆区重新申请空间,进行拷贝操作。

示例

class Person
{public:int m_age = 0;int* m_height = NULL;
public:Person(){cout << "Person默认构造函数被调用" << endl;}Person(int age,int height){m_age = age;m_height = new int(height);cout << "Person有参构造函数被调用" << endl;}//编译器写的拷贝构造函数是浅拷贝//浅拷贝带来的问题是堆区开辟的数据重复释放//浅拷贝的问题要利用深拷贝来解决//自己实现拷贝构造函数 解决浅拷贝带来的问题Person(const Person& p){cout << "自己写的Person拷贝构造函数被调用" << endl;m_age = p.m_age;//m_height = p.m_height;//编译器默认的是这个代码//深拷贝m_height = new int(*p.m_height);}~Person(){//析构代码,将堆区开辟数据做释放操作if (m_height != NULL){delete m_height;m_height = NULL;}cout << "Person析构函数被调用" << endl;}
};
void test()
{Person p1(18,160);cout << "p1的年龄为:" << p1.m_age << "p1的身高为:" << *p1.m_height << endl;Person p2(p1);cout << "p2的年龄为:" << p2.m_age << "p2的身高为:" << *p2.m_height << endl;
}
int main()
{test();system("pause");return 0;
}

总结

析构函数中的代码可以写 将堆区开辟数据做释放操作

如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

4.2.6 初始化列表

作用

C++提供了初始化列表语法,用来初始化属性

语法

构造函数(): 属性1(值1), 属性2(值2) ... { }

示例

class Person
{public:int m_A;int m_B;int m_C;
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){}
};
void test1()
{//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()
{test1();system("pause");return 0;
}

4.2.7 类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为对象成员

例如:

class A {};
class B
{A a;
};

B类中有对象A作为成员,A为对象成员。

示例

class Phone
{public:string m_phone_Name;
public:Phone(string phone_Name){m_phone_Name = phone_Name;cout << "Phone构造函数被调用" << endl;}~Phone(){cout << "Phone的析构函数被调用" << endl;}
};
class Person
{public:string m_Name;Phone m_Phone;
public:Person(string name, string phone_Name) :m_Name(name), m_Phone(phone_Name){cout << "Person构造函数被调用" << endl;}~Person(){cout << "Person的析构函数被调用" << endl;}
};
void test()
{Person p("张大庆", "小米");cout << p.m_Name << "拿着:" << p.m_Phone.m_phone_Name << endl;
}
int main()
{test();system("pause");return 0;
}

构造的顺序是:先调用对象成员的构造,再调用本类构造
析构的顺序与构造相反

4.2.8 静态成员

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

静态成员分为:

  • 静态成员变量

    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成员函数

    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量

示例1:静态成员变量

class Person
{public:static int m_A;//静态成员变量也是有访问权限的
private:static int m_B;
};
//类内声明,类外初始化操作
//编译阶段就分配内存
int Person::m_A = 100;
int Person::m_B = 1000;void test1()
{Person p1;cout << p1.m_A << endl; //100Person p2;p2.m_A = 200;//所有对象都共享同一份数据cout << p1.m_A << endl;//用p1访问变成200
}
void test2()
{//静态成员变量 不属于某个对象上 所有对象都共享同一份数据//因此静态成员变量有两种访问方式//1.通过对象进行访问//Person p;//cout << p.m_A << endl;//100//2.通过类名进行访问cout << Person::m_A << endl;//100 //cout << Person::m_B << endl;//类外访问不到私有静态成员变量
}
int main()
{//test1();test2();system("pause");return 0;
}

示例2:静态成员函数

class Person
{public:static int m_A;//静态成员变量int m_B;
public:static void func1(){m_A = 100;//静态成员函数可以访问 静态成员变量 //m_B = 1000;//静态成员函数不可以访问 非静态成员变量//无法区分是哪个对象的m_B的属性cout << "static void func1()的调用" << endl;}//静态成员函数也是有访问权限的
private:static void func2(){cout << "static void func1()的调用" << endl;}
};
int Person::m_A = 0;
//有两种访问方式
void test1()
{//通过对象访问Person p;p.func1();//通过类名访问Person::func1();//Person::func2();//类外访问不到私有静态成员函数
}
int main()
{test1();system("pause");return 0;
}

4.3 C++对象模型和this指针

4.4 友元

4.5 运算符重载

4.6 继承

4.7 多态


最后更新于 2023/4/7 暂时停更,预计2023/8/1恢复更新

C++|从进阶到精通相关推荐

  1. 《BREW进阶与精通——3G移动增值业务运营、定制与开发》一书的网店地址

    电子工业出版社 http://www.phei.com.cn/bookshop/bookinfo.asp?bookcode=TN089390&booktype=main 当当网 http:// ...

  2. arduino 智能车组装步骤_【本周福利】arduino从入门、进阶到精通学习资料包(免费滴)...

    本周福利来了 21ic网友arduinoxyz在论坛无私分享自己做项目时积累的宝贵资料,转发在此,以帮助更多需要的人. arduinoxyz: 入坑3年有余,积累了一些arduino的资料教程.部分是 ...

  3. 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之44---QChat 技术...

    从本质上说,POC/PTT技术是把VoIP技术应用于移动终端,将原来由运营商承担的部分VoIP功能转移到手机终端来实现.因此,POC可参照传统的网络分层概念.其中,POC系统中的移动信道层.应用层以及 ...

  4. 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之76——BREW中的安全性网络编程...

    安全性的网络编程主要是通过SSL实现的,首先要创建创建SSLRoot,可用的根证书通常是常用的VeriSign 根证书.它们可以节省手持设备上的空间,并且仅保留一个副本,而不是在每个使用 SSL 的应 ...

  5. 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之31---LBS基于BREW的位置服务...

    移动位置服务(LBS--Location Based Service)是利用一定的技术手段通过移动网络获取移动终端用户的位置信息(经纬度坐标),在电子地图平台的支持下,为用户提供相应服务的一种增值业务 ...

  6. 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之26---礼品卡支付...

    除了用户通过BREW中的移动商店购买应用之外,其他丰富的付费方式(实际上是指购买方式)无疑能够提高移动增值业务的市场穿透能力.在零售行业中,礼品卡或者购物券是非常普遍的促销方式(图6-8所示). 90 ...

  7. 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之70---面向照相机的开发...

    这里主要讲述一下面向手机中照相机的应用开发.ICamera能够使应用访问手机中的照相机传感器,配置照相机的快照和录像模式,并提供多种的记录和编码的方式(图14-9). 图14-9:ICamera的体系 ...

  8. 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之22---BDS的分发流程...

    BREW通过一个安全的企业外联网站点使运营商对应用的选择.管理.定价.用户的使用跟踪和计费拥有完全的控制权.从商业模式上来讲,BREW应用的分发主要有两种: 一种是运营商控制的应用的分发,另一种是典型 ...

  9. 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之6---移动增值业务概述

    移动增值业务(Mobile Added Value Service)就是在移动通信网上开发运行除了语音等基本业务以外的服务类型.目前国际上通常把移动增值业务概括为两大类,即移动话音增值业务和移动数据增 ...

  10. 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之9---移动增值业务产业链

    移动增值业务领域中的产业链突破了传统语音增值业务中移动运营商的限制,其产业链扩展至:设备制造商.运营商.业务运营商(SP).内容提供商(CP)和最终用户等市场主体,逐步创造多方共赢的商业模式: (1) ...

最新文章

  1. 百度Apollo发布海量自动驾驶数据集,还有两项重磅挑战赛
  2. 散列算法 SHA-1,SHA-2和SHA-256之间的区别
  3. LINUX内核分析第四周——扒开系统调用的三层皮
  4. 从头到脚说单测——谈有效的单元测试
  5. ChartCtrl源码剖析之——CChartAxis类
  6. python删除过期文件_python删除过期文件的方法
  7. ubuntu14.04matlab2015b 测试caffe的Matlab接口
  8. Git笔记(23) 不同角色的贡献
  9. JQuery日记_5.14 Sizzle选择器(七)
  10. 解决 jq ui 弹框 select2 input 失效问题
  11. java 填充图片_java图片缩放实现图片填充整个屏幕
  12. 代做assignment分享高分Essay写作攻略
  13. java 加减乘除_加减乘除运算(Java)
  14. Euclidean algorithm
  15. nRF24L01的发送性能优化
  16. 手机应用游戏开发死机现场之一
  17. python在化工模拟中的应用_python完成模拟博客园登陆
  18. (私人收藏)商务工作学习万能简约大气PPT模板
  19. python如何实现语音识别
  20. 信号与系统(二):拉普拉斯变换的意义:谈H(s)、h(t)、δ(t)

热门文章

  1. Appium App UI自动化测试
  2. 办理车辆示廊灯E-mark认证要做些什么准备?
  3. 互联网广告生态圈介绍
  4. Redis的数据恢复
  5. java随机yujie_排序算法的总结——Java实现
  6. 5天成交6个亿,中国春季云车展凭什么吸粉?
  7. win10 休眠不读u盘_电脑休眠唤醒后无法使用USB键盘如何解决,小编告诉你解决电脑休眠唤醒后无法使用USB键盘...
  8. 校园招聘-2017滴滴研发工程师内推笔试编程题
  9. 【Arduino与HC-SR04超声波传感器握手完整教程】
  10. php 按时间区间查询,Laravel数据库指定时间区间查询 按天按小时查询数据