快捷键 | 说明 |
---|---|
F9 | 切换断点 |
Ctrl+F5 | 开始执行(不调试) |
F5 | 开始调试、继续(跳过一个断点) |
Shift+F5 | 停止调试 |
Ctrl+shift+F5 | 重启调试 |
F10 | step over |
F11 | step into 逐步运行 |
Shift+F11 | step out |
Ctrl+J | 智能提示 |
Tab | 选择和使用智能提示内容 |
(Ctrl+M) , M | 保持Ctrl不松开,先按下M,再按下M ;折叠或展开当前方法 |
(Ctrl+M) , O | 保持Ctrl不松开,先按下M,再按下O ;折叠所有 |
(Ctrl+M), L | 保持Ctrl不松开,先按下M,再按下L ;展开所有 |
/*
源文件扩展名:cpp
入口:main函数
java必须先有类,再有方法(函数)
/* . */ 左边只能是对象
//为什么内存里面的值默认的都是ccccccc -858993460
中断:interrupt
cc->int3:起到断点的作用,防止代码执行到函数的时候出现乱码
*/
#define _CRT_SECURE_NO_WARNINGS//防止在使用C的方法时报错
#include
using namespace std;
struct Person{
int m_age;
};
struct Student:Person{
int m_no;
};
int main()
{
Student student;
cout<<""<
//1.全局变量检测增强
int a;
int a=10;
//2.函数检测增强,参数类型增强,返回值检测增强,函数调用参数检测增强
int getRectS(w,h){
return w*h
}
void test(){
getRects(10,10,10)
}
//3.类型转换检测增强,在C++里面需要强转(char*)
void test(){
char *p = malloc(sizeof(93)) //malloc返回值是void*
}
//4.struct增强
struct Person{
int m_Age;
void plusAge();//c语言中strut不可以加函数,但C++中可以
}
void test01(){
struct Person p1;//c语言中使用时候必须加入struct关键字
Person p2;//c++中使用时候可以不用写struct关键字
}
//5.bool类型增强 C语言中没有bool类型
bool flag = true;
void test(){
cout<b?a:b)<b?a:b)=100;//C++返回的是变量
cout<<" a="<
//C语言const默认外部链接
//文件 test.c
//extern const int a = 10; C++语言外部链接需要添加extern
const int a = 10;//C语言const默认外部链接
int main(){
extern const int a;//告诉编译器去外部查找这个
print("a=%d\n",a)
}
c++中
//const int a = 10;默认是内部链接
//C++语言使用外部链接,需要添加extern提高作用域
extern const int a = 10;
int main(){
extern const int a;//告诉编译器去外部查找这个
cout<
//1、const分配内存,取地址会分配临时内存
//2、extern 编译器会给const变量分配内存
void test01(){
const int a = 10;
int *p=(int*)&a;//分配临时内存 int temp = a; int *p=(int*)&temp;
}
//3、使用变量初始化const的变量
void test02(){
int a = 1;
const int b = a;//会分配内存
//分配内存的变量都可以使用指针取修改它
int *p=(int*)&b;
}
//自定义数据类型 (string) 加const会分配内存
struct Person
{
string m_Name;
int m_Age;
}
void test03(){
const Person p1;
//p1.m_Name = "a" 不可以修改
Person *p = (Person*)&p1;//指针可以修改
p->m_Name = "";
p->m_Age = 10;
cout<<"m_Age:"<
全局变量:g_
成员变量:m_
静态变量:s_
常量:c_
驼峰标识
作用域运算符
int atk =200;
void test(){
int atk = 100;
cout<<"局部"<
/*
源文件扩展名:cpp
入口:main函数
java必须先有类,再有方法(函数)
/* . */ 左边只能是对象
//为什么内存里面的值默认的都是ccccccc -858993460
中断:interrupt
cc->int3:起到断点的作用,防止代码执行到函数的时候出现乱码
*/
#include
using namespace std;
struct Person{
int m_age;
};
struct Student:Person{
int m_no;
};
int main()
{
Student student;
cout<<""<
全局变量:g_
成员变量:m_
静态变量:s_
常量:c_
驼峰标识
很多调试信息,生成可执行文件比较臃肿
去除调试信息,生成可执行文件比较精简、高效
会优化代码
在release模式下,右击属性》禁止优化
发布项目使用这个模式
每个应用都有自己独立的内存空间,其内存空间一般都有以下几大区域
应用 | 栈空间 | 堆空间 | 代码区 | 全局区 | ||||||
---|---|---|---|---|---|---|---|---|---|---|
微信 | ||||||||||
浏览器 | ||||||||||
每调用一个函数就会给它分配一段连续的栈空间,等函数调用完毕之后会自动回收这段栈空间,用于存放局部变量
自动分配和回收
需要主动申请和释放
在程序运行的过程中,能够自由控制内存的生命周期,大小
用于存放代码
用于存放全局变量,整个程序运行都存在
//MJ 命名空间
namespace MJ{
int g_age;
void func(){
}
class Person{
int m_age;
int m_money;
}
}
int main(){
using namespace MJ;
//从这里开始往下 都可以直接使用 MJ空间下的成员,可以省略MJ::
g_age = 20;
}
#include
using namespace std;
//命名空间嵌套
namespace AA {
int g_age;
void func() {
cout << "这是AA" << endl;
}
namespace BB {
namespace CC {
int g_age;
}
}
}
void func() {
cout << "这是全局" << endl;
}
int main() {
using namespace AA;
::func();//调用全局的func
::AA::func();//调用AA的func
//方式1
AA::BB::CC::g_age = 20;
AA::g_age = 30;
//方式2
using namespace AA;
using namespace AA::BB::CC;
AA::g_age = 40;
//AA::BB::g_age = 40;//AA::BB::下没有g_age成员 报错
AA::BB::CC::g_age = 40;
//方式3
using AA::BB::CC::g_age;
g_age = 50;
return 0;
}
//命名空间合并
namespace CC {
int g_age;
}
namespace CC {
int g_name;
}
//等价于
namespace CC {
int g_age;
int g_name;
}
Extern |
---|
被extern “C” 修饰的代码会按照C语言的方式去编译 |
用于C和C++混合开发 |
#include
using namespace std;
//extern "C" void func() {
//
//}
//extern "C" void func(int v1) {
//
//}
//extern "C" {
// void func() {
//
// }
// void func(int v1){
//
// }
//}
//extern "C" {
// void func();
// void func(int v);
//}
//以上会报错 c语言不允许重载
//不会报错 ,这个不属于重载,属于不同的函数
//void func();
//extern "C" void func(int v);
extern "C" void func();
extern "C" void func(int v);
int main() {
return 0;
}
void func() {
cout << "func()" << endl;
}
void func(int v) {
cout << "func(int)" << endl;
}
名称 | 解释 |
---|---|
头文件 | 专门用来声明函数的文件 |
源文件 | 专门用来实现声明的文件 |
引入头文件 | #include “文件名.h”; |
宏定义 | 它的作用是在编译预处理时,将源程序中所有"宏名"替换成右边的"字符串",常用来定义常量。 一般宏 #define 宏名 字符串 比如:#define PI 3.14 … #undef PI PI在#undef是无效的 |
特殊宏 | #define __MATH_H |
#pragma once | 防止头文件的内容被重复包含、兼容性不好、老编译器不支持,只针对整个文件 |
#ifndef 、#define、#endif | 防止头文件的内容被重复包含、不受任何编译器的限制,可以针对某段代码 |
#include
using namespace std;
/*
第三方库:C语言写的库
*/
//引用头文件
#include "math.h";
#include "test.h";
int main() {
cout << sum(10, 20) << endl;
cout << delta(10, 20) << endl;
cout << divide(10, 0) << endl;
test();
return 0;
}
c++头文件
//test.h
void test();
c++文件
//test.cpp
//引入库
#include
using namespace std;
#include "math.h";
void test() {
cout<<"test():"<
C语言文件
//math.c
//引入C语言头文件stdio.h
#include
int sum(int v1, int v2) {
return v1 + v2;
}
int delta(int v1, int v2) {
return v1 - v2;
}
int divide(int v1, int v2) {
if (!v2)
{
printf("v2不能为%d",v2);
return v2;
}
return v1 / v2;
}
//math.h
//宏定义
//如果没有定义__MATH_H 就定义 __MATH_H
#ifndef __MATH_H
#define __MATH_H
#ifdef __cplusplus
extern "C" {
#endif
int sum(int v1, int v2);
int delta(int v1, int v2);
int divide(int v1, int v2);
#ifdef __cplusplus
}
#endif
#endif
//给类型起别名,最好使用typedef
typedef int Area;
using Sum = int;
Area a;
Sum b;
#include
typedef char *String;
int main(int argc, const char * argv[]) {
// 相当于char *str = "This is a string!";
String str = "This is a string!";
printf("%s", str);
return 0;
}
//typedef与#define
typedef char *String1;
#define String2 char *
int main(int argc, const char * argv[]) {
String1 str1, str2;
String2 str3, str4;
return 0;
}
//在这种情况下,只有str1、str2、str3才是指向char类型的指针变量,str4只是个char类型的变量。
//**类型(表达式);**
int(z);
//**(类型)表达式;**
(int)z;
//**类型转换操作符<类型>(表达式**)
static_cast(z);
类型转换操作符:const_cast、dynamic_cast、reinterpret_cast、static_cast
#include
using namespace std;
//定义枚举类型
enum GameResult{WIN,LOSE,TIE,CANCEL};
int main(){
GameResult result;
enum GameResult omit = CANCEL;
for(int count = WIN;count<=CANCEL;count++){
result = GameResult(count);
if(result == omit)
cout<<"The game was cancelled" << endl;
else{
count<<"The game was played!!";
if(result == WIN) cout<<"and we won!";
if(result == LOSE) cout<<"and we lost!";
cout<
auto:编译器通过初始值自动推断变量类型
decltype:定义一个变量与某个表达式的类型相同,但并不使用改表达式的初始化变量的值
操纵符 | 含义 |
---|---|
<< | 预定义的插入符,作用在流类对象cout上便可以实现向标准输出设备输出; cout<<表达式<<表达式… |
>> | 标准输入是将提取符作用在流类对象cin上 cin>>表达式>>表达式 |
dec | 数值采用十进制表示 |
hex | 数值采用16进制表示 |
oct | 数值采用8进制表示 |
ws | 提取空白符 |
endl | 插入换行符,并刷新流 |
ends | 插入空字符 |
setsprecision(int) | 设置浮点数的小数位数(包括小数点) |
setw(int) | 设置域宽 |
cout<
指针里面存的是个内存地址
数组是一个指针
没有被赋值的指针是一个野指针,野指针容易导致程序崩溃
对野指针赋值是不允许的,比如:*p = 10;
&变量名
数据类型 * 指针名 = 某
个地址 ;
*指针名
指针1 = 指针2; | 这两个指针指向指针2存储的同一个地址 |
---|---|
数组1 = 指针2; |
int arr[5];
int *p = &arr[0];//指针指向第一个数组的地址
p+1;//arr[1] 往后推移一个元素 int类型向后移动4个字节 char往后移动是1个字节
for(int i;i<5;i++)
{
//打印p+1
print(p);
p++;//
}
int *p = new int;//编译器会开辟出一个内存空间
*p = 10;
delete p;//删除内存/释放内存/系统回收内存,p又恢复了野指针
p = null;//解决野指针,让p指向空
//如果再次使用new语句之后,之前的空间没有使用delet,则之前的空间就会一直占用
p = new int;
所以要记得释放内存
int len;
len = 2;
int arr[len]//报错:这种形式声明的数组,长度是不能动态赋值
int len;
len = 2;
int *arr = new int[len];//建立数组指针,一次性申请len个内存,就是数组
//通过下标可以访问数组的各个成员
for(int i = 0;i
int len =3;
int**arr = new int*[len];//创建二维指针
//可以通过arr[i] = new int[n];创建一维指针
for(int i = 0;i|
赋值不会建立引用关系,只有初始化才会;
short n = 10;//引用与被引用只要一方发生变化,另一个也会发生变化
short&m = n;//初始化只能是变量,
short x = 15;//引用只有在初始化的时候起作用
m = x;//赋值的话,m没有成为x的引用
m+=5;//
//m=20 n=20 x=15;
void func1(int a,void(*p)(int)){
cout<<"func1:"<
引用的本质就是指针,只是编译器削弱了它的功能,所以引用就是弱化了的指针
//指针相当于给age起了一个 别名:*p
int age = 10;
int height = 30;
int *p = &age;
*p = 20;//相当于age = 20;
*p = height//age = height;
p = &height;//指针可以更改指向
cout<<"age="<
被const修饰的变量不可修改
如果修饰的是类、结构体(的指针),其成员不可以更改
#include
using namespace std;
str
int main() {
const int age = 20;
age = 39;
return 0;
}
const修饰的其右边的内容不可更改
//const修饰的其右边的内容
#include
using namespace std;
struct Date
{
int year;
int month;
int day;
};
int main() {
Date d1 = { 2020,2,31 };
Date d2 = { 2020,2,3 };
const Date* p = &d1;
Date* const q = &d1;
cout << p->day << endl;
/*p->day = 20;
(*p).day = 30;*/
p = &d2;
//p1 = &d2;
(*q).day = 10;
cout << p->day << endl;
cout << q->day << endl;
(*p).day = 30;//报错 指针指向的内容不可以更改
int age;
int height;
//p1不是常量,*p1是常量
const int* p1 = &age;
//p2不是常量,*p2是常量
int const* p2 = &age;
*p1 = 29;//报错 指针指向的内容不可以更改
p1 = &height;
*p2 = 23;//报错 指针指向的内容不可以更改
//p3是常量,*p3不是常量
int* const p3 = &age;
*p3 = 30;
p3 = &height;//报错 指针指向容不可以更改
*p3 = 39;
//p4是常量,*p4是常量
const int* const p4 = &age;
//p5是常量,*p5是常量
int const* const p5 = &age;
*p4 = 30;//报错 指针指向的内容不可以更改
p4 = &height;//报错 指针指向容不可以更改
p5 = &height;//报错 指针指向容不可以更改
*p5 = 39;//报错 指针指向的内容不可以更改
return 0;
}
引用可以被const修饰,这样就无法通过引用修改数据了,但可以访问,可以成为常引用
可以指向临时数据(常量、表达式、函数返回值等)
//常量
int age = 10;
int &ref = 30//报错
const int &ref = 30//加上个const就可以了
//表达式
int a = 1;
int b = 2;
int &c = a+b;//报错
const int &c = a+b//加上个const就可以了
可以指向不同类型的数据
//表达式
double a = 1;
const int &ref = a;//报错
作为函数参数时(此规则也适用于const指针)
int sum(int &v1,int &v2){
return v1+v2;
}
int main(){
int a = 10;
int b = 20;
sum(a,b);//非const实参
sum(20,10);//常量实参 报错
return 0;
}
//使用const引用,就可以接受常量实参
int sum(const int &v1,const int &v2){
return v1+v2;
}
int main(){
int a = 10;
int b = 20;
const int c = 10;
const int d = 20;
//非const实参
sum(a,b);//非const实参
sum(20,10);//常量实参
//const实参
sum(c,d);
return 0;
}
//枚举类型
//不限定作用域的枚举类型
enum Weekday
{SUN,MON,TUE,WED,TUE,THU,FRI,SAT};
//默认值 0 1 2 3...
//1.先定义枚举类型,再定义枚举变量
enum Season {spring, summer, autumn, winter};
enum Season s;
//2.定义枚举类型的同时定义枚举变量
enum Season {spring, summer, autumn, winter} s;
//3.省略枚举名称,直接定义枚举变量
enum {spring, summer, autumn, winter} s;
#include
using namespace std;
int main()
{
//字符串 方式1
const char* name = "bmw";
//字符串 方式2
char name2[] = {'b','m','w','\0'};//必须以\0 结尾,这是终止符
//字符串长度 比字符个数 多1
cout << strlen(name2) << endl;
//数组名称 也是个指针,值等于首地址
char *p1 = name2;
char *p2 = &name2[0];//name2 的首地址 即name[0]的地址
return 0;
}
//头文件
#pragma once
class Person
{
int m_age;
public:
void setAge(int age);
int getAge();
Person();
~Person();
};
//实现
#include "Person.h"
void Person::setAge(int age) {
}
int Person::getAge() {
return m_age;
}
Person::Person() {
m_age = 0;
}
Person::~Person() {
}
//引入Person.h 头文件
#include
using namespace std;
#include "Person.h"
int main()
{
Person person;
person.setAge(20);
person.getAge();
return 0;
}
当一个整体由多个数据构成时,我们可以用数组来表示这个整体,但是数组有个特点:内部的每一个元素都必须是相同类型的数据。
在实际应用中,我们通常需要由不同类型的数据来构成一个整体,比如学生这个整体可以由姓名、年龄、身高等数据构成,这些数据都具有不同的类型,姓名可以是字符串类型,年龄可以是整型,身高可以是浮点型。
为此,C语言专门提供了一种构造类型来解决上述问题,这就是结构体,它允许内部的元素是不同类型的。
/*
struct 结构体名{
类型名1 成员名1;
类型名2 成员名2;
...
类型名n 成员名n;
};
*/
//结构体内部的元素,也就是组成成分,我们一般称为"成员"。
//struct是关键字,是结构体类型的标志。
struct Student {
char *name; // 姓名
int age; // 年龄
float height; // 身高
};
//初始化
struct Student stu = {"MJ", 27};
//前面只是定义了名字为Student的结构体类型,并非定义了一个结构体变量,就像int一样,只是一种类型。
//定义结构体变量方式:struct 结构体名 变量;
struct Student stu;
stu = {"MJ", 27};
cout << stu.name << endl;
stu.age = 27;// 访问stu的age成员
/*
结构体指针变量的定义形式:
struct 结构体名称 *指针变量名
有了指向结构体的指针,那么就有3种访问结构体成员的方式
结构体变量名.成员名
(*指针变量名).成员名
指针变量名->成员名
*/
int main(int argc, const char *argv[]) {
// 定义一个结构体类型
struct Student {
char *name;
int age;
};
struct Student stu = {"MJ", 27}; // 定义一个结构体变量 &
struct Student *p; //定义一个指向结构体的指针变量
p = &stu; // 指向结构体变量stu
//指针指向对象
//这时候可以用3种方式访问结构体的成员
// 方式1:结构体变量名.成员名
printf("name=%s, age = %d \n", stu.name, stu.age);
// 方式2:(*指针变量名).成员名
printf("name=%s, age = %d \n", (*p).name, (*p).age);
// 方式3:指针变量名->成员名
printf("name=%s, age = %d \n", p->name, p->age);
return 0;
}
//类和结构体的区别就是:类默认的访问权限是private,结构体默认的访问权限是public
//类
class Mj{
private://默认
int m_age;
void run(){}
}
//结构体
struct Mj{//默认
public:
int m_age;
void run(){}
}
你想把它当做int 还是 char 取决于你,
//x86环境中
//申请4个字节空间,你想把它当做int 还是 char 取决于你
int *p = (int *)malloc(4);//返回首字节地址,使用指针接收 需要强转
*p = 10;
//回收free()
free(p);//只能回收一次
//char类型只占一个字节,
char *p = (char *)malloc(4);//返回首字节地址,使用指针接收
*p = 10;//只赋值了一个字节 相当于 p[0] = 10;
*(p+1) = 11;//p[1] = 11;
*(p+2) = 12;//p[2] = 12;
*(p+3) = 13;//p[3] = 13;
//回收free()
free(p);//只能回收一次 不会调用析构函数
你想把它当做int 还是 char 取决于你
int *p = new int;
*p = 10;
delete p;
//申请一个字节
char *p = new char;
*p = 10;
delete p;
//char类型数组
char *p = new char[4];//申请4个字节
delete[] p;
int size = sizeof(int) * 10;
int *p = (int *)malloc(size);
//mymory set
//将每个字节设置为0
memset(p,0,size);
//memset(p,1,4);
// 00000001 00000001 00000001 00000001
int *p1 = new int;
int *p2 = new int();//初始化为0 带有小括号 会调用memset
int *p3 = new int(5);//初始化为5 带有小括号 会调用memset
int *p4 = new int[3];//申请3个int类型大小的空间,未初始化
int *p5 = new int[3]();//申请3个int类型大小的空间,初始化为0 带有小括号 会调用memset
int *p6 = new int[3]{};//申请3个int类型大小的空间,初始化为0 带有大括号 会调用memset
int *p7 = new int[3]{5};//申请3个int类型大小的空间,第一个元素被初始化为5,其它元素被初始化为0 带有大括号 会调用memset
//带有小括号 会调用memset
成员访问权限、继承有3种方式
public:公共的,任何地方都可以访问(struct默认)
protected:子类内部,当前类内部可以访问
private:私有的,当前类内部可以访问(class默认)
#include
using namespace std;
struct Person
{
public:
int m_age;
private:
int m_name;
};
//这种方式实际开发中不会这么用,这样会很乱,GoodStudent就不会继承到 m_age
struct Student:private Person
{
//以私有的方式继承Person
/*
相当于
private:
int m_age;
*/
};
//子类访问父级成员变量的权限,取决于
//父类本身的访问权限和上一级父类的继承方式
struct GoodStudent :public Student {
void work() {
m_age = 20;//报错
}
};
//public是最常用的继承方式
int main()
{
}
#include
using namespace std;
struct Person{
private:
int m_age;
public:
int getAge() {
return m_age;
}
};
struct Student:public Person{
};
//继承方式不影响GoodStudent的大小
struct GoodStudent : Student {
void work() {
//m_age = 20;
int m_age = getAge();
}
};
int main()
{
//继承方式不影响GoodStudent的大小
GoodStudent sd;
cout << sizeof(sd) << endl;
}
#include
using namespace std;
class Person{
int m_age;
public:
int getAge() {
return m_age;
}
};
class Student:public Person{
};
//继承方式不影响GoodStudent的大小
class GoodStudent :public Student {
void work() {
//m_age = 20;
int m_age = getAge();
}
};
int main()
{
//继承方式不影响GoodStudent的大小
GoodStudent sd;
cout << sizeof(sd) << endl;
}
对象的内存可以存放3个地方 | |
---|---|
全局区 | 全局变量 |
堆空间 | 动态申请(malloc、new等) |
栈空间 | 函数里面的局部变量 |
//结构体
struct Mj{//默认
//成员变量
int m_age;
int m_name;
//成员方法
void run(){}
}
int main(){
Mj mj; // 定义一个结构体变量 &
Student *p; //定义一个指向结构体的指针变量
p = &mj; // 指向结构体变量stu
//指针指向对象
//这时候可以用3种方式访问结构体的成员
// 方式1:结构体变量名.成员名
printf("name=%s, age = %d \n", mj.m_name, mj.m_age);
// 方式2:(*指针变量名).成员名
printf("name=%s, age = %d \n", (*p).m_name, (*p).m_age);
// 方式3:指针变量名->成员名
printf("name=%s, age = %d \n", p->m_name, p->m_age);
}
struct Person(){
int m_id;
int m_age;
int m_height;
void run(){}
}
int main(){
Person person;
person.m_id = 1;
person.m_age = 2;
person.m_height = 3;
}
内存地址 | 内存数据 | ||||||||
---|---|---|---|---|---|---|---|---|---|
&person | &person.m_id | 0x00E69B60 | 1 | ||||||
0x00E69B61 | |||||||||
0x00E69B62 | |||||||||
0x00E69B63 | |||||||||
&person.m_age | 0x00E69B64 | 2 | |||||||
0x00E69B65 | |||||||||
0x00E69B66 | |||||||||
0x00E69B67 | |||||||||
&person.m_height | 0x00E69B68 | 3 | |||||||
0x00E69B69 | |||||||||
0x00E69B6A | |||||||||
0x00E69B6B |
#include
using namespace std;
struct Person {
int m_age;
/*
* 能够访问成员变量的方式
默认会把 person1对象的地址值传过来,Person *person = &person1;
this -> person
void run(Person *person) {
cout << "Person::run() - " << person->m_age << endl;
}*/
void run() {
//this 指针存储这函数调用者的地址
//this 指向了函数调用者
//this = &person1;
//this->m_age;
cout << "Person::run() - " << this->m_age << endl;
}
};
int main()
{
Person person1;//栈空间
person1.m_age = 20;
person1.run();//代码区
Person person2;
person2.m_age = 40;
person2.run();
return 0;
}
//栈空间
//堆空间
//代码区
//全局区
#include
using namespace std;
/*内联函数:将函数调用直接展开为函数代码*/
inline void func();
inline int sum(int v1, int v2);
//开辟栈空间
void func() {
cout << "func()1" << endl;
cout << "func()2" << endl;
cout << "func()3" << endl;
}
//回收栈空间
//调用频繁、代码体积少,适合使用内联函数
//尽量不要超过10行
inline int sum(int v1, int v2) {
return v1 + v2;
}
//递推不会变成内敛 死循环
inline void run() {
run();
}
int main() {
func();
func();
func();
int c = sum(10, 29);//不会开辟栈空间
int c = 10+29;
return 0;
}
#include
using namespace std;
//函数的重载 name mangling/name decoration 底层技术
//C语言没有函数重载
int sum(int v1, int v2) {
return v1 + v2;
}
int sum(int v1, int v2,int v3) {
return v1 + v2+v3;
}
//返回值类型相同不能构成重载 --歧义/二义性
//int sum(double v1) {
//
//}
//实参的隐式转换可能产生二义性
//使用重载name mangling技术编译之后变成:display_long
void display(long a) {
}
//使用重载name mangling技术编译之后变成:display_double
void display(double a) {
}
//使用重载name mangling技术编译之后变成:display_int
void display(int a) {
}
int main() {
cout << sum(10, 20) << endl;
cout << sum(10, 20, 30) << endl;
//display(10); 产生二义性
return 0;
}
//在release模式下,右击属性》禁止优化 可以看到函数重载的技术原理
#include
using namespace std;
int age = 20;
//默认参数 必须是从右到左
void func(int v1,int v2,int v3 = 20,int v4)//报错
//如果函数同时有声明、实现、默认参数只能放在函数声明中
//默认参数的值可以是常量、全局符号(全局变量、函数名)
int sum(int v1, int v2 = 20) {
return v1 + v2;
}
int sum(int v1, int v2,double v3 = age) {
return v1 + v2 + v3;
}
void func1(int a,void(*p)(int)){
cout<<"func1:"<
函数重载。默认参数可能会产生冲突、二义性(建议优先选择使用默认参数)
int sum(int v1) {
return v1;
}
int sum(int v1, int v2 = 20) {
return v1 + v2;
}
int main() {
sum(10);//这里会报错
return 0;
}
#include
using namespace std;
struct Person
{
int m_age;
int m_height;
/*Person(int age,int height) {
m_age = age;
m_height = height;
}*/
/*Person(int age, int height) : m_age(age),m_height(height)
{
}*/
int func() {
return 0;
}
int f_age() {
cout << " f_age()" << endl;
return 0;
}
int f_height() {
cout << " f_height()" << endl;
return 0;
}
//初始化顺序和成员变量定义的顺序保持一致
Person(int age, int height) : m_age(f_age()), m_height(f_height())
{
//输出结果
// f_age()
// f_height()
}
};
int main()
{
Person person0(23,180);
cout << person0.m_age << endl;
cout << person0.m_height << endl;
}
如果声明和实现分离
初始化列表只能写在实现中
默认参数只能写在声明中
#include
using namespace std;
struct Person
{
int m_age;
int m_height;
int m_id;
//声明
Person(int age = 0, int height = 0, int id = 0);
};
//实现
Person::Person(int age, int height, int id) : m_age(age), m_height(height), m_id(id)
{
m_age = 10;
}
int main()
{
Person person;
Person person0(180);
Person person1(23,180);
Person person2(23,180,23);
cout << person0.m_age << endl;
cout << person0.m_height << endl;
}
#include
using namespace std;
//特点 函数名与类同名,无返回值(void都不能写),可以有参数,可以重载,可以有多个构造函数
//一旦定义了构造函数,必须使用其中一个自定义的构造函数来初始化对象
struct Person {
int m_age;
int m_name;
int m_height;
//构造函数
//没有返回值 自动调用
Person() {
cout << sizeof(this) << endl;//4
memset(this, 0, sizeof((*this)));//*this是操作this指向的对象 是person
cout << "Person()\n" << m_age<m_age = 20;
delete p1;
//通过malloc创建 不会调用构造函数
Person* p2 = (Person*)malloc(sizeof(Person));
free(p2);
//对象和指针访问方式的不同
Person person5;
Person *person6 = new Person;
person5.m_age = 20;//对象 使用 点
person6->m_age = 20;//指针 使用 箭头
}
注意:通过malloc申请的空间不会调用构造函数
只有再特定情况下,才会调用默认构造函数
https://www.bilibili.com/video/BV1Lo4y1o717?p=49
//不会调用默认构造函数的情况
#include
using namespace std;
struct Person {
int m_age;
};
int main()
{
Person person5;
person5.m_age = 10;
}
//调用默认构造函数的情况
#include
using namespace std;
struct Person {
int m_age = 10;//区别在这里
};
int main()
{
Person person5;
person5.m_age = 10;
}
如果有自定义构造函数,则除了全局对象,其它的对象都不会初始化
https://www.bilibili.com/video/BV1Lo4y1o717?p=51
//有自定义构造函数
#include
using namespace std;
struct Person {
int m_age;
Person() {
// memset(this, 0, sizeof(Person));//全部初始化 成员变量为0
}
Person(int age) {
// memset(this, 0, sizeof(Person));//全部初始化 成员变量为0
}
};
Person g_person0;//成员变量都初始化0
Person g_person1();//函数声明
Person g_person2(10);//
int main()
{
//如果有自定义构造函数,则除了全局对象,其它的对象都不会初始化
//栈空间
Person person5;//成员变量不会初始化
//堆空间
//成员变量都不会初始化
Person* person6 = new Person;
Person* person7 = new Person();
Person* person8 = new Person[3];
Person* person9 = new Person[3]();//3个Person对象 的成员变量都不会初始化
Person* person10 = new Person[3]{};//3个Person对象 的成员变量都不会初始化
//打印结果---是否初始化为0
cout << g_person0.m_age << endl;//0 是
cout << g_person2.m_age << endl;//0 是
cout << person6->m_age << endl;//842150451 否
cout << person7->m_age << endl;//842150451 否
cout << (*(person8)).m_age << endl;//842150451 否
cout << (*(person8 + 1)).m_age << endl;//842150451 否
cout << (*(person9)).m_age << endl;//842150451 否
cout << (*(person9 + 1)).m_age << endl;//842150451 否
cout << (*(person10)).m_age << endl;//842150451 否
cout << (*(person10 + 1)).m_age << endl; //842150451 否
}
//没有自定义构造函数
#include
using namespace std;
struct Person {
int m_age;
};
Person g_person0;//成员变量都初始化0
Person g_person1();//函数声明
int main()
{
//如果没有定义构造函数,并且没有初始化成员变量
Person person0;//直接报错
Person person1();//函数声明
//栈空间
//前提:如果没有定义构造函数,直接报错
Person person5;//成员变量不会初始化
//堆空间
//前提:如果没有定义构造函数
Person *person6 = new Person;//成员变量不会初始化
Person *person7 = new Person();//成员变量都初始化0
Person *person8 = new Person[3];//不会成员变量不会初始化
Person *person9 = new Person[3]();//3个Person对象 的成员变量都初始化0
Person* person10 = new Person[3]{};//3个Person对象 的成员变量都初始化0
}
作用:优化代码,让重复的初始化操作简化
#include
using namespace std;
struct Person
{
int m_age;
int m_height;
//构造函数调用构造函数,必须在初始化列表里面调用
Person() : Person(0,0){
//创建了一个临时的Person对象
//Person(20, 20); 返回一个person对象
//这样写,这个调用有参构造传给this的对象地址是一个临时的地址,并不是person的地址;所以初始化的成员变量不是person对象的成员变量。
//等价于 => 以下写法
//Person person; //这样创建对象默认不会调用构造函数,不会产生递归 https://www.bilibili.com/video/BV1Lo4y1o717?p=49
//person.m_age = 20;
//person.m_height = 20;
}
Person(int age, int height) {
this->m_age = age;
this->m_height = height;
}
};
int main()
{
Person person;
cout << "person.m_age = " << person.m_age << endl;
cout << "person.m_height = " << person.m_height << endl;
return 0;
}
父类如果有无参构造函数和有参构造函数,子类的构造函数默认会调用父类的无参构造函数
父类如果没有无参的构造函数,但是有有参构造函数,就必须调用有参构造函数,否则报错
如果父类没有构造函数,就不会调用构造函数,汇编代码里面不会出现call Person::Person()
//方式1
#include
using namespace std;
struct Person {
int m_age;
Person() {
//m_age = 0;
cout << "Person::Person()" << endl;
}
Person(int age) {
cout << "Person::Person(int age)" << endl;
}
};
struct Student :Person {
int m_no;
//父类如果有无参构造函数和有参构造函数,子类的构造函数默认会调用父类的无参构造函数
//父类如果没有无参的构造函数,但是有有参构造函数,就必须调用有参构造函数,否则报错
//如果父类没有构造函数,就不会调用构造函数,汇编代码里面不会出现call Person::Person()
Student():Person(10) {//子类调用父类的有参构造函数
//call Person::Person()
cout << "Student::Student()" << endl;
}
};
int main()
{
Student student;
cout << "" << endl;
return 0;
}
//调用父类构造函数
//子类调用父类的构造函数,来初始化父类的成员变量
#include
using namespace std;
class Person {
int m_age;
public:
Person(int age):m_age(age) {}
};
class Student :Person {
int m_no;
public:
//如何初始化age,需要调用父类构造函数
Student(int age,int no) : m_no(no),Person(age) {}//调用父类构造函数
};
int main()
{
Student student(23,23);
return 0;
}
#include
using namespace std;
struct Car
{
Car() {
cout << "Car::Car()" << endl;
}
~Car() {
cout << "Car::~Car()" << endl;
}
};
struct Person
{
int m_age;
Car* m_car;
Person() {
m_age = 20;
m_car = new Car;//在堆里面,不会自动销毁,对象内部申请的堆空间
cout << "Person::Person()" << endl;
}
//专门用来做内存清理工作的
~Person() {
delete m_car;//对象内部申请的堆空间,由对象内部回收
cout << "Person::~Person()" << endl;
}
};
int main()
{
{
Person *person = new Person ;//在堆里面,不会自动销毁
delete person;
}
{
Person person;
}
return 0;
}
//子类调用父类析构函数
#include
using namespace std;
class Person {
int m_age;
public:
Person(int age):m_age(age) {}
~Person(){
cout << "Person::~Person()" << endl;
}
};
//
class Student :Person {
int m_no;
public:
Student(int age,int no) : m_no(no),Person(age) {}
//先调用子类的析构函数
~Student() {
/*
.....
*/
cout << "Student::~Student()" << endl;
//再调用父类的析构函数
//call Person::~Person();
}
};
int main()
{
{
Student student(23, 23);
}
return 0;
}
#include
using namespace std;
//继承
//父类 超类 superclass
struct Person
{
int m_age;
void run(){}
};
//子类、派生类 subclass
struct Student : Person
{
int m_score;
int no;
void study() {
}
};
struct GoodStudent:Student
{
int m_money;
};
struct Worker : Person
{
int m_salary;
void work() {
}
};
int main()
{
Student student;
student.m_age = 20;
student.m_score = 90;
student.run();
student.study();
GoodStudent gs;
//内存布局:
//gs 继承了 Person 和 Student 相当于把 Person和Student的成员直接放到GoodStudent中
//struct GoodStudent :Student
//{
// int m_age;
// int m_score;
// int no;
// int m_money;
// 方法是放在代码区的
//};
cout << "Hello World!\n";
}
c++允许一个类可以有多个父类(不建议使用,会增加程序设计复杂度)
#include
using namespace std;
struct Student {
int m_score;
Student(int score):m_score(score){}
void study() {
cout << "Student::study()-m_score=" << m_score << endl;
}
};
struct Worker {
int m_salary;
Worker(int salary) :m_salary(salary) {}
void work() {
cout << "Worker::work()- m_salary=" << m_salary << endl;
}
};
//先继承Student 后继承Worker
//成员变量的顺序和继承顺序一直
struct Undergraduate : Student, Worker {
int m_grade;
Undergraduate(int score,int salary,int grade) : Student(score), Worker(salary), m_grade(grade){
}
void play() {
cout << "Undergraduate::play()-grade=" << m_grade << endl;
}
};
int main()
{
Undergraduate ug(99,4000,4);
/*ug.m_grade = 4;
ug.m_salary = 4000;
ug.m_score = 99;*/
ug.play();
ug.study();
ug.work();
return 0;
}
#include
using namespace std;
struct Person{
private:
int m_age;
public:
void setAge(int age){
if(age<=0) return;
m_age = age;
}
int setAge() {
return m_age;
}
};
int main(){
Person person;
person.setAge(-4);
person.setAge(29);
cout<
一个对象的多种形态;
struct Person{
int m_age;
}
struct Student:Person{
int m_score;
}
//父类指针 指向 子类对象
Person* person = new Student(); //这里申请一个Student类型的堆空间,有成员变量m_age,m_score
//但是这里只能访问父类的成员变量,操作安全,因为这是Person类型的指针
person->m_age;//
//假设子类指针指向父类对象
//这里申请一个Person类型的堆空间,只有成员变量m_age
Student*student = (Student *)new Person();//进行强转 student类型,骗过编译器
student->m_age;
//则这里可以访问到m_score,因为这是Student类型的指针
student->m_score;//这里操作就很危险,因为这里访问了不属于自己的内存空间
//默认情况下,编译器只会根据指针类型调用对应的函数,不存在多态
//多态是面向对象非常重要的一个特性
//同一个操作作用于不同的对象,可以有不同的解释,产生不同的执行结果
//在运行时,可以识别出真正的对象类型,调用对应子类中的函数
#include
using namespace std;
struct Animal {
//父类的函数是个虚函数,子类重写函数的也是个虚函数
virtual void speak() {
cout << "Animal::speak()" << endl;
}
virtual void run() {
cout << "Animal::run()" << endl;
}
};
struct Cat: Animal {
//子类重写父类的成员函数
void speak() {
cout << "Cat::speak()" << endl;
}
void run() {
cout << "Cat::run()" << endl;
}
};
struct Dog : Animal {
//重写(覆写、覆盖、override) 返回值 函数名 参数 都一样
void speak() {
cout << "Dog::speak()" << endl;
}
void run() {
cout << "Dog::run()" << endl;
}
};
struct Pig : Animal {
void speak() {
cout << "Pig::speak()" << endl;
}
void run() {
cout << "Pig::run()" << endl;
}
};
//父类指针指向子类对象
void Liu(Animal *animal) {
animal->speak();//利用父类指针调用(重写的)成员函数
animal->run();
delete animal;
}
int main()
{
Liu(new Dog());
Liu(new Cat());
Liu(new Pig());
cout << "" << endl;
return 0;
}
//在产生多态的时候,虚表的地址值就会存放在对象的内存的前4个字节空间中,当调用函数的时候
Liu(new Cat());//new Cat()申请堆空间,对象占12字节 前4个字节是虚表
//虚表的地址:0xB89B64
//虚表里面存放是对象的函数的地址值
//内存地址值0xB89B64存放的是Cat::speak()的函数地址->0x00B814E7
//内存地址值0xB89B68存放的是Cat::run()的函数地址->0x00B814CE
原理:找到指针对象所指向的存储空间,取出里面的前面四个字节(虚表的地址),通过虚表找到函数,取出对应的函数地址值
//如果没有重写虚函数,虚表里面存放的地址还是父类原来的虚函数地址
#include
using namespace std;
struct Animal {
int m_age;
//父类的函数是个虚函数,子类重写函数的也是个虚函数
virtual void speak() {
cout << "Animal::speak()" << endl;
}
virtual void run() {
cout << "Animal::run()" << endl;
}
};
struct Cat : Animal {
//子类重写父类的成员函数
int m_height;
void run() {
cout << "Cat::run()" << endl;
}
void speak() {
cout << "Cat::speak()" << endl;
}
Cat() {
cout << "Cat::Cat()" << endl;
}
~Cat() {
cout << "Cat::~Cat()" << endl;
}
};
//父类指针指向子类对象
void Liu(Animal* animal) {
animal->speak();//利用父类指针调用(重写的)成员函数
animal->run();
cout << sizeof(*animal) << endl;//8个字节 虚表+m_age
delete animal;
}
int main()
{
Cat* c = new Cat();//虚表+m_age+m_height
Animal* a = new Cat();//虚表+m_age 虚表:存放的是 Cat::run() Cat::speak()
Liu(c);
new Cat();
cout << sizeof(*(new Cat())) << endl;//12 虚表+m_age+m_height
cout << sizeof(new Cat()) << endl;//4 new Cat()的地址
cout << sizeof(Cat) << endl;//12 虚表+m_age+m_height
cout << sizeof(*c) << endl;//12 虚表+m_age+m_height
cout << sizeof(*a) << endl;//8 虚表+m_age
//new Cat() 是个地址
return 0;
}
//如果没有重写虚函数,虚表里面存放的地址还是父类原来的虚函数地址
#include
using namespace std;
struct Animal {
//父类的函数是个虚函数,子类重写函数的也是个虚函数
virtual void speak() {
cout << "Animal::speak()" << endl;
}
virtual void run() {
cout << "Animal::run()" << endl;
}
};
struct Cat: Animal {
//子类重写父类的成员函数
void run() {
cout << "Cat::run()" << endl;
}
};
//父类指针指向子类对象
void Liu(Animal *animal) {
animal->speak();//利用父类指针调用(重写的)成员函数
animal->run();
delete animal;
}
int main()
{
Liu(new Cat());
return 0;
}
//内存地址值0xB89B64存放的是Animal::speak()的函数地址->0x00B814E7
//内存地址值0xB89B68存放的是Cat::run()的函数地址->0x00B814CE
#include
using namespace std;
/*
*/
class Person {
int m_age;
public:
Person() {
}
virtual void run() {
cout << "Person" << endl;
}
};
class Student :public Person{
public:
int m_score = 100;
/*编译器会在默认构造函数里面 把 m_score = 100;*/
void run() {
cout << "Student" << endl;
}
};
int main()
{
Person *person = new Student();
cout<m_age = 1;
c->m_height = 2;
c->run();
c->speak();
c->run();
cout << sizeof(Cat) << endl;
//0x0067ED60
//68 ab f4 00 00f4ab68
/*
72 11 f4 00 00F41172
1e 10 f4 00 00F4101E
00 00 00 00
43 61 74 3a
3a 72 75 6e
*/
Student student;
return 0;
}
struct Animal {
//父类的函数是个虚函数,子类重写函数的也是个虚函数
virtual void speak() {
cout << "Animal::speak()" << endl;
}
virtual void run() {
cout << "Animal::run()" << endl;
}
};
struct Cat: Animal {
//子类重写父类的成员函数
void run() {
Animal::run();//调用父类成员函数
cout << "Cat::run()" << endl;
}
};
//当存在父类对象指向子类对象的时候,也就是多态的时候,需要把父类的析构函数声明为虚析构函数,如果不是,则delete cat的时候,则不会调用Cat的析构函数
struct Animal {
//父类的函数是个虚函数,子类重写函数的也是个虚函数
virtual void speak() {
cout << "Animal::speak()" << endl;
}
virtual void run() {
cout << "Animal::run()" << endl;
}
//构造函数
Animal(){
cout << "Animal::Animal()" << endl;
}
//虚-析构函数
virtual ~Animal(){
cout << "Animal::~Animal()" << endl;
}
};
struct Cat: Animal {
//子类重写父类的成员函数
void run() {
Animal::run();//调用父类成员函数
cout << "Cat::run()" << endl;
}
//构造函数
Cat(){
//Call Animal::Animal
cout << "Cat::Cat()" << endl;
}
//析构函数
~Cat(){
cout << "Cat::~Cat()" << endl;
//Call Animal::~Animal
}
};
int main(){
Animal *cat = new Cat();
cat->speak();
cat->run();
delete cat;
return 0;
}
没有函数体且初始化为0的虚函数,用来定义接口规范
#include
using namespace std;
struct Animal {//抽象类
//纯虚函数
virtual void speak() = 0;
virtual void run() = 0;
};
struct Cat : Animal {//抽象类--因为子类没有完全重写纯虚函数
void run() {
cout << "Cat::run()" << endl;
}
};
//只要在KafeiCat之前的所有类中,加一起都重写了纯虚函数,那么KafeiCat就不是抽象类
//
struct KafeiCat : Cat {
void run() {
cout << "KafeiCat::run()" << endl;
}
void speak() {
cout << "KafeiCat::speak()" << endl;
}
};
int main()
{
Animal anim;//报错 不可以创建对象
Animal* kafeicat = new KafeiCat();// 报错 不可以创建对象
kafeicat kafei;
cout << "" << endl;
return 0;
}
#include
using namespace std;
struct Student {
int m_score;
Student(int score):m_score(score){}
virtual void study() {
cout << "Student::study()-m_score=" << m_score << endl;
}
};
struct Worker {
int m_salary;
Worker(int salary) :m_salary(salary) {}
virtual void work() {
cout << "Worker::work()- m_salary=" << m_salary << endl;
}
};
//先继承Student 后继承Worker
//成员变量的顺序和继承顺序一直
struct Undergraduate : Student, Worker {
int m_grade;
Undergraduate(int score,int salary,int grade) : Student(score), Worker(salary), m_grade(grade){
}
void play() {
cout << "Undergraduate::play()-grade=" << m_grade << endl;
}
void study() {
cout << "Undergraduate::study()-m_score=" << m_score << endl;
}
void work() {
cout << "Undergraduate::work()- m_salary=" << m_salary << endl;
}
};
int main()
{
//工作者
Worker* worker = new Undergraduate(33, 4000, 5);
worker->work();
//学生
Student* student = new Undergraduate(22, 4000, 2);
student->study();
//工作和学生
Undergraduate ug(99,4000,4);
ug.play();
ug.study();
ug.work();
return 0;
}
#include
using namespace std;
struct Student {
int m_score;
Student(int score):m_score(score){}
virtual void study() {
cout << "Student::study()-m_score=" << m_score << endl;
}
};
struct Worker {
int m_salary;
Worker(int salary) :m_salary(salary) {}
virtual void work() {
cout << "Worker::work()- m_salary=" << m_salary << endl;
}
};
//先继承Student 后继承Worker
//成员变量的顺序和继承顺序一直
struct Undergraduate : Student, Worker {
int m_grade;
Undergraduate(int score,int salary,int grade) : Student(score), Worker(salary), m_grade(grade){
}
void play() {
cout << "Undergraduate::play()-grade=" << m_grade << endl;
}
void study() {
cout << "Undergraduate::study()-m_score=" << m_score << endl;
}
void work() {
cout << "Undergraduate::work()- m_salary=" << m_salary << endl;
}
};
int main()
{
//工作者
Worker* worker = new Undergraduate(33, 4000, 5);
worker->work();
//学生
Student* student = new Undergraduate(22, 4000, 2);
student->study();
//工作和学生
Undergraduate ug(99,4000,4);
ug.play();
ug.study();
ug.work();
return 0;
}
解决菱形继承基类成员变量二义性的问题
#include
using namespace std;
struct Person {
int m_age = 1;
};
//虚继承
struct Student :virtual Person {
//int m_age = 1;
int m_score = 2;
};
//虚继承
struct Worker :virtual Person {
//int m_age = 1;
int m_salary = 3;
};
struct Undergraduate : Student, Worker {
/*
如果没有虚继承,继承过来相当于这样:
int m_age = 1;
int m_score = 2;
int m_age = 1;
int m_salary = 3;
这时,m_age会被重复继承,如果有了虚继承:成员变量的空间布局
Student类-虚表-偏移量 0 -20(从本类其实位置到基类成员变量的位置)
int m_score
Worker类-虚表-偏移量 0 -12(从本类其实位置到基类成员变量的位置)
int m_salary
int m_grade
m_age //基类Person的m_age会被放到最下边 ,通过偏移量可以找到同一个m_age
*/
int m_grade = 4;
};
int main()
{
Undergraduate ug;
ug.m_age = 10;
return 0;
}
#include
using namespace std;
//Java开发:接口
//iOS开发:协议
//抽象类
class JobBaomu {
public:
virtual void clean() = 0;
virtual void cook() = 0;
};
class JobTeacher {
public:
virtual void playFootBall() = 0;
virtual void playBaseBall() = 0;
};
class Student : public JobBaomu,public JobTeacher{
int m_socre;
public:
void clean() {
}
void cook() {
}
};
class Worker : public JobTeacher {
int m_salary;
public:
void playFootBall() {
}
void playBaseBall() {
}
};
int main()
{
cout << "Hello World!\n";
return 0;
}
#include
using namespace std;
class Car {
int m_age;
protected://Car和子类访问
int m_score;
public:
static int m_price;
static void run() {
cout << "Car::run()" << endl;
//this 这里面是不可能有this
//m_age = 0; //没有对象也可以访问这个方法,没有对象哪来的成员对象
}
};
int m_price;//谁都可以访问
//在类外边初始化成员变量
int Car::m_price = 0;
int main()
{
Car::run(); //这里并没有创建对象,不存在对象
Car car1;
car1.m_price = 100;
car1.run();
Car car2;
car2.m_price = 200;
Car car3;
car3.m_price = 300;
//m_price 只存在一份,相当于全局变量
cout << car1.m_price << endl;//300
cout << car2.m_price << endl;//300
cout << car3.m_price << endl;//300
Car* car4 = new Car();
car4->m_price = 400;
cout << car4->m_price << endl;//400
delete car4;
cout << Car::m_price << endl;//400
}
#include
using namespace std;
/*
* 单例模式:设计模式的一种,保证某个类永远只创建一个对象
* 1.构造函数私有化
*/
class Rocket {
private:
static Rocket* ms_rocket;//Rocket保证对象唯一
Rocket() {}//构造函数私有化,外界无法创建对象
~Rocket() {}//析构函数私有化,外界无法销毁对象
public:
static Rocket *sharedRocket() {
if (ms_rocket == NULL) {
ms_rocket = new Rocket();
}
return ms_rocket;
}
void run() {
cout << "Rocket::run()" << endl;
}
static void deleteRocket() {
//这里考虑线程安全问题
if (ms_rocket == NULL) {
delete ms_rocket;
ms_rocket = NULL;//不清空会产生野指针
}
}
};
Rocket* Rocket:: ms_rocket = NULL;
class RedRocket :public Rocket {
public:
/*RedRocket() {
}*/
};
int main()
{
Rocket *p1 = Rocket::sharedRocket();
Rocket *p2 = Rocket::sharedRocket();
Rocket *p3 = Rocket::sharedRocket();
Rocket *p4 = Rocket::sharedRocket();
Rocket *p5 = Rocket::sharedRocket();
Rocket* p6 = p1->sharedRocket();
// delete p6;
return 0;
}
const成员:被const修饰的成员变量、非静态成员函数
const成员变量
const成员函数(非静态)
#include
using namespace std;
/*
##### const引用
引用可以被const修饰,这样就无法通过引用修改数据了,但可以访问,可以成为常引用
##### const引用特点
可以指向临时数据(常量、表达式、函数返回值等)
*/
class Car {
private:
int m_x;
int m_y;
public:
static int m_age;//静态成员变量 必须要初始化
static const int m_age1 = 0;//这样可以写
static const int m_age2 = 1;
const int m_price = 10;//const成员变量 必须要初始化 这是方式1
const int m_price1;//必须要初始化
/* Car() :m_price1(0) {
}*/
Car(int peice ) :m_price1(peice) {}//const成员变量 必须要初始化 这方式2
//const成员函数 声明
void display() const;
//静态成员函数
static void test() {
//这里是不可以访问成员变量
//m_x;//非静态的成员变量不可以访问
m_age;//静态的可以访问
}
//const成员函数
void test2() const{
}
void test1() {
m_x = 209;
//m_price = 29;
}
};
//const成员函数 实现
void Car::display() const{//*内部不能修改非static成员变量**
test();//静态成员函数 可以访问
test2();//const成员函数 可以访问
//test1();//const成员函数不可以访问非const成员函数
//不可以修改成员变量,可以访问
cout << "(" << m_x << "," << m_y << ")" << endl;
}
int Car::m_age = 20;//静态成员变量 必须在外边初始化
int main()
{
Car car(0);
return 0;
}
拷贝构造函数是构造函数的一种特殊情况
当利用已存在的对象,创建一个新对象时(类似于拷贝),就会调用新对象的拷贝构造函数进行初始化;
#include
using namespace std;
class Car {
int m_price;
int m_length;
public:
Car(int price = 0, int length = 0) : m_price(price),m_length(length) {
cout<<"Car(int price = 0, int length = 0)"<m_price = car.m_price;
this->m_length = car.m_length;
*/
/*
拷贝操作,相当于:
m_price = car.m_price;
m_length = car.m_length;
*/
}
void display() {
cout<<"m_price:"<
拷贝构造函数2种调用方式
#include
using namespace std;
class Car {
int m_price;
int m_length;
public:
Car(int price = 0, int length = 0) : m_price(price),m_length(length) {
cout<<"Car(int price = 0, int length = 0)"<
#include
using namespace std;
class Person {
int m_age;
public:
//父类-构造函数
Person(int age):m_age(age) {
cout <<"Person(int age):m_age(age)" <
编译器默认的提供的拷贝是浅拷贝
//初始化指针成员变量所遇到的问题
#include
using namespace std;
//存在的问题
class Car {
private:
int m_price;
char *m_name;
public:
Car(int price = 0,const char* name = NULL) :m_price(price), m_name(name) {
}
void display() {
cout << "m_price:" << m_price << "\nm_name:" << m_name << endl;
}
};
Car* g_car2;
void test() {
//堆空间指向栈空间
//这个操作很危险
char name2[] = { 'b','m','w','\0' };
g_car2 = new Car(101, &name2[0]); //堆空间中的m_name存储的地址值是 局部变量name2的值
//当调用完test()之后,局部变量被自动回收,m_name变成野指针
}
int main()
{
//字符串 方式1
const char* name = "bmw";
//字符串 方式2
char name2[] = {'b','m','w','\0'};
//字符串实际长度 比字符个数 多1
//cout << strlen(name2) << endl;
//数组名称 也是个指针,值等于首地址
Car* car1 = new Car(100, name2);
Car* car2 = new Car(101, &name2[0]);
car1->display();
car2->display();
test();
g_car2->display();//m_price:101 m_name:烫烫烫烫烫烫烫烫H靨
return 0;
}
//存在的问题
void test() {
//堆空间指向栈空间
//这个操作很危险
char name2[] = { 'b','m','w','\0' };
g_car2 = new Car(101, &name2[0]); //堆空间中的m_name存储的地址值是 局部变量name2的值
//当调用完test()之后,局部变量被自动回收,m_name变成野指针
}
//解决堆空间指向栈空间的问题
/*
思路:把传给构造函数的 临时数据 保存到堆空间中,让指针指向该空间,这样即使临时变量被销毁,堆空间的也不会消失。
操作:修改m_name的指向,不在指向栈空间name2的地址,而是指向新申请的堆空间的地址
把传入的参数复制到改堆空间中,使用方法strcpy(dist,src)->从src复制到dist;
*/
#include
using namespace std;
class Car {
private:
int m_price;
char *m_name;
public:
/************************解决问题的核心代码*************************/
Car(int price = 0,const char* name = NULL) :m_price(price){
if (name!=NULL)
{
//申请新的堆空间
m_name = new char[strlen(name)]{};
//拷贝字符串数据到新的堆空间
strcpy(m_name, name);
}
}
//拷贝构造
//(把成员变量的值进行拷贝,并不会考虑指针指向的堆空间,产生野指针),会导致多次堆空间free的问题
Car(const Car &car):m_price(car.m_price) {
}
void display() {
cout << "m_price:" << m_price << "\nm_name:" << m_name << endl;
}
~Car() {
//清除堆空间
if (m_name!=NULL)
{
delete[] m_name;
m_name = NULL;
}
}
};
/*^^^^^^^^^^^^^^^^^^^^^^^^解决问题的核心代码^^^^^^^^^^^^^^^^^^^^^^^^*/
Car* g_car2;
void test() {
char name2[] = { 'b','m','w','\0' };
g_car2 = new Car(101, &name2[0]);
}
int main()
{
test();
g_car2->display();//打印正常
return 0;
}
如果需要实现深拷贝,就需要自定义拷贝构造函数:
将指针类型的成员变量所指向的内存空间,拷贝到新的内存空间
#include
using namespace std;
/*
浅拷贝(shallow copy):指针类型的变量只会拷贝地址值;(把成员变量的值进行拷贝,并不会考虑指针指向的堆空间,产生野指针),会导致多次堆空间free的问题
https://www.bilibili.com/video/BV1Lo4y1o717?p=87 5分钟
深拷贝(deep copy):将指针指向的内容copy到新的存储空间;
*/
class Car {
private:
int m_price;
char *m_name;
void copy(const char* name = NULL) {
if (name != NULL)
{
//申请新的堆空间
m_name = new char[strlen(name)+1]{};
//拷贝字符串数据到新的堆空间
strcpy(m_name, name);
}
}
public:
//构造函数
Car(int price = 0,const char* name = NULL) :m_price(price){
copy(name);
}
//拷贝构造函数 //深拷贝
Car(const Car &car):m_price(car.m_price) {
copy(car.m_name);
}
void display() {
cout << "m_price:" << m_price << "\nm_name:" << m_name << endl;
}
~Car() {
if (m_name!=NULL)
{
delete[] m_name;
m_name = NULL;
}
}
/*有bug 解决 -》软件问题
void setName(const char* name = NULL) {
if (name != NULL)
{
delete[] m_name;
m_name = NULL;
//cout << name << endl;
//cout << m_name << endl;
copy(name);
}
}*/
};
int main()
{
Car car3(100,"BMW");
Car car4 = car3;
car4.display();
//car3.setName("BENCE");
car3.display();
return 0;
}
#include
using namespace std;
/*
浅拷贝(shallow copy):指针类型的变量只会拷贝地址值;(把成员变量的值进行拷贝,并不会考虑指针指向的堆空间)
深拷贝(deep copy):将指针指向的内容copy到新的存储空间;
1、构造的时候把指针指向的内容放到堆空间
2、当产生拷贝构造的时候,第一步产生堆空间里面的内容进行复制到另一个堆空间。
*/
class Car {
private:
int m_price;
char *m_name;
//复制一个字符到另一个新的堆空间
void copy(const char* name = NULL) {
if (name != NULL)
{
cout <<"name:" <
对象类型的返回值一定会产生额外的对象
注意:返回对象类型的引用最好返回的是调用者本身this所指向的对象,如果返回的是函数内部创建的对象的引用(地址),会出现问题。
#include
using namespace std;
//不要返回对象类型:原因是会产生额外的对象
class Car {
public:
Car() {
cout << "Car::Car()"<< this << endl;
}
Car(const Car &car) {
cout << "Car(const Car &car)" << this << endl;
}
void run() {
cout << "Car::run()" << endl;
}
};
void test1(Car car) {//对象作为参数
}
void test2(Car &car) {//引用作为参数
}
void test3(Car* car) {//指针作为参数
}
/*
从函数的栈空间 把car 对象传递给 main函数的栈空间,需要一次拷贝构造
在main函数里面 再次传给新的对象 也需要一次拷贝构造 但编译器优化了
*/
Car test4() {//实际是:2次拷贝构造 1次正常构造 编译器优化了一次拷贝构造
Car car;//2、自动拷贝构造一个新的对象放到main函数内存里面
return car;
}
/*
这里返回的是一个 Car类型的对象,并不是原来的car5对象,而是新的对象
*/
//返回值是一个临时对象Car Temp = *car;
Car test5(Car* car) {
return *car;
}
//返回值是个引用 也是个对象,不过没有产生新对象
Car &test6(Car* car) {
return *car;
}
int main()
{
Car car1;
test1(car1);//产生拷贝构造 1次拷贝构造
test2(car1);//不会产生
Car* car2 = &car1;
test3(car2);
Car car3;
//2次拷贝构造 1次正常构造 编译器优化了一次拷贝构造
car3 = test4();//如何把test4中的car放到main函数里面的?:1、传给test4一个main函数的地址
//这里产生了拷贝构造 ,就是说 有新的对象产生才会调用了拷贝构造函数
Car car5;
test5(&car5);//这样会产生 只是没有利用
Car car6 = test5(&car5);//这里利用了
//这里没有产生新对象,这样才一直使用的是car7对象的地址
Car car7;
test6(car7);
return 0;
}
证明:
#include
using namespace std;
//不要返回对象类型:原因是会产生额外的对象
class Car {
public:
Car() {
cout << "Car::Car()"<< this << endl;
}
Car(const Car &car) {
cout << "Car(const Car &car)" << this << endl;
}
void run() {
cout << "Car::run()" << endl;
}
~Car() {
cout << "Car::~Car" << this << endl;
}
};
void test1(Car car) {
//对象作为参数
//Car car = car1; 所以这是拷贝构造
//Car(const Car &car)0133F868
//Car::~Car0133F868
}
void test2(Car &car) {//引用作为参数
}
void test3(Car* car) {//指针作为参数
}
Car test4() {//实际是:2次拷贝构造 1次正常构造 编译器优化了一次拷贝构造
//Car::Car()0069F66B
Car car;//2、自动拷贝构造一个新的对象放到main函数内存里面
return car;
//Car::~Car0069F66B 这个是test4中的car
}
Car test5(Car* car) {
return *car;
}
int main()
{
Car car1;//Car::Car()0133F98B
test1(car1);//产生拷贝构造 1次拷贝构造 1次析构
cout << 1 << endl;
test2(car1);//不会产生
Car* car2 = &car1;
test3(car2);//不会产生
cout << 2 << endl;
//Car::Car()0133F973
Car car3;
//2次拷贝构造 1次正常构造 编译器优化了一次拷贝构造;
//Car(const Car &car)0069F6BB 这个是拷贝到main函数中的 car
car3 = test4();//如何把test4中的car放到main函数里面的?:1、传给test4一个main函数的地址
//Car::~Car0069F6BB 这个是拷贝到main函数中的 car
return 0;
}
//main结束 会调用两次析构
//2次析构
//Car::~Car0069F793 car3
//Car::~Car0069F7AB car1
没有变量名,没有被指针指向的对象,用完后马上调用析构
临时对象不会产生拷贝构造 编译器做了优化
/*匿名对象*/
//临时对象不会产生拷贝构造 编译器做了优化
cout << 1 << endl;
//Car::Car()0069F6AF
Car car3 = Car();
//Car::~Car0069F6AF
cout << 2<< endl;
c++中存在隐式构造的现象:某些情况下,会隐式调用单参数的构造函数
#include
using namespace std;
//通过关键字explicit 禁止隐式构造
class Person {
int m_age;
public:
Person() {
cout << "Person::Person()" << this << endl;
}
explicit Person(int age):m_age(age) {
cout << "Person::Person(int age)" << this << endl;
}
Person(const Person& person) {
cout << "Person(const Person &person)" << this << endl;
}
void display() {
cout << "Person::run()"<
总结:对象创建后,需要做一些额外操作时,比如内存操作,函数调用,编译器一般会为其自动创建构造函数
#include
using namespace std;
/*
很多教程都说:编译器会为每一个类都生成空的无参的构造函数
在特定情况下,编译器自动生成空的无参构造函数:比如
* 成员变量在声明的同时初始化
*
*/
class Person {
public:
Person() {
}
};
class Student {
public:
int m_score = 100;
/*编译器会在默认构造函数里面 把 m_score = 100;*/
};
int main()
{
Person person;
Student student;
return 0;
}
友元包括友元函数和友元类
如果将函数A(非成员函数)声明为类C的友元函数,那么在函数A内部就能直接访问类C对象的所有成员
如果将类A声明为类C的友元类,那么在类A的所有成员函数内部都能直接访问类C对象的所有成员
#include
using namespace std;
class Point {
//声明友元函数
friend Point add(Point, Point);
//声明友元类
friend class Math;
private:
int m_x;
int m_y;
public:
int getX() { return m_x; };
int getY() { return m_y; };
Point(int x, int y) :m_x(x), m_y(y) {}
void display() {
cout << "(" << m_x << "," << m_y << ")" << endl;
}
};
//友元类
class Math {
public:
Point add(Point p1, Point p2) {
return Point(p1.m_x + p2.m_x, p1.getY() + p2.getY());
}
void test() {
Point p1(2,3);
p1.m_x = 20;
}
};
//友元函数 非成员函数
Point add(Point p1, Point p2) {
return Point(p1.m_x + p2.m_x, p1.getY() + p2.getY());
}
int main()
{
Point p1(20, 30);
Point p2(20, 30);
Point p3 = add(p1, p2);
p3.display();
return 0;
}
如果将类A定义在类C的内部,那么类A就是内部类(嵌套类)
#include
using namespace std;
class Person {
int m_age;
static int ms_height;
static void test() {
}
public:
//内部类不影响 外部类内存布局,只是内部类访问权限的改变
class Car {
int m_age;
public:
void run() {
Person person;
person.m_age = 20;//内部类可以访问外部类所有的成员(反过来不行)
//内部类可以省略对象名和类名直接访问静态成员
ms_height = 30;
test();
}
};
};
int main()
{
Person::Car car1;
Person::Car car2;
Person p;
return 0;
}
声明和实现分离
#include
using namespace std;
//方式1
//声明
class Point {
class Math;
};
//实现
class Point::Math{
test(){
}
}
//方式2
//声明
class Point {
class Math;
};
class Point::Math{
test();
}
//实现
class Point::Math::test(){
}
//方式3
//声明
class Point {
class Math{
void test();
};
};
//实现
void Point::math::test(){
}
#include
using namespace std;
void test() {
static int age = 10;
static int ms_age = 10;
//局部类
class Car {
int ms_age;//
void run() {
age = 29; //报错
}
};
}
int main()
{
return 0;
}
运算符重载:为运算符增加新的功能
#include
using namespace std;
class Point {
//声明友元函数
friend Point operator+(Point, Point);
private:
int m_x;
int m_y;
public:
int getX() { return m_x; };
int getY() { return m_y; };
Point(int x, int y) :m_x(x), m_y(y) {}
void display() {
cout << "(" << m_x << "," << m_y << ")" << endl;
}
};
//友元函数 非成员函数
Point operator+(Point p1, Point p2) {
cout <<"operator+(Point, Point)" <
优化
#include
using namespace std;
/*
##### const引用
引用可以被const修饰,这样就无法通过引用修改数据了,但可以访问,可以成为常引用
##### const引用特点
可以指向临时数据(常量、表达式、函数返回值等)
*/
class Point {
//声明友元函数
friend Point operator+(const Point &, const Point &);
private:
int m_x;
int m_y;
public:
int getX() { return m_x; };
//const成员函数
int getY() const { return m_y; };
Point(int x, int y) :m_x(x), m_y(y) {}
void display() {
cout << "(" << m_x << "," << m_y << ")" << endl;
}
};
//友元函数 非成员函数 const成员变量
Point operator+(const Point &p1,const Point &p2) { //既可以接受常量,又可以接受对象
cout <<"operator+(Point, Point)" <
在优化
#include
using namespace std;
class Point {
private:
int m_x;
int m_y;
public:
Point(int x, int y) :m_x(x), m_y(y) {}
void display() {
cout << "(" << m_x << "," << m_y << ")" << endl;
}
//既可以接受常量,又可以接受对象
Point operator+(const Point& point) {
cout << "operator+(Point)" << endl;
return Point(m_x + point.m_x,m_y + point.m_y);
}
};
int main()
{
Point p1(20, 30);
const Point p2(20, 30);
Point p3(20, 30);
Point p4 = p1 + p2 + p3;//运算符重载 //Point p5 = operator+(p1, p2);
p3.display();
p4.display();
return 0;
}
增加减法
#include
using namespace std;
class Point {
private:
int m_x;
int m_y;
public:
Point(int x, int y) :m_x(x), m_y(y) {}
void display() {
cout << "(" << m_x << "," << m_y << ")" << endl;
}
//既可以接受常量,又可以接受对象
Point operator+(const Point& point) {
return Point(m_x + point.m_x,m_y + point.m_y);
}
Point operator-(const Point& point) {
return Point(m_x - point.m_x, m_y - point.m_y);
}
Point(const Point &point) {
cout <<"拷贝构造" <
临时对象不会产生拷贝构造
原因:编译器做了优化
#include
using namespace std;
class Point {
private:
int m_x;
int m_y;
char m_z;
public:
Point(int x, int y,char z) :m_x(x), m_y(y),m_z(z) {
cout << "构造函数" << m_z << this << endl;
}
void display() {
cout << "(" << m_x << "," << m_y << ")" << endl;
}
//既可以接受常量,又可以接受对象
Point operator+(const Point& point) {
return Point(m_x + point.m_x,m_y + point.m_y, m_z);
}
Point operator-(const Point& point) {
return Point(m_x - point.m_x, m_y - point.m_y, m_z);
}
Point(const Point &point) {
cout <<"拷贝构造" << this <
泛型,是一种将 [类型参数化] 以达到代码复用的技术,C++中使用模板来实现泛型
模板使用格式如下
template T add(T a,T b){
return a+b;
}
//T 将来可以发生变化
#include
using namespace std;
class Point {
friend ostream& operator<<(ostream&, const Point&);
int m_x;
int m_y;
public:
Point(int x,int y):m_x(x),m_y(y){}
const Point operator+(const Point &point) {
return Point(m_x+point.m_x,m_y+point.m_y);
}
};
ostream &operator<<(ostream &cout, const Point& point) {
cout << "("< T add(T a,T b) {
return a + b;
}
/*
这种写法也可以
template T add(T a,T b) {
return a + b;
}
*/
/*
相当把类型当做参数
template T add(T a,T b) {
return a + b;
}
*/
int main()
{
add(10, 20);
cout << add(10, 20) << endl;
cout << add(Point(10, 20), Point(10, 20)) << endl;
cout << add(10, 20) << endl;
cout << add(Point(10, 20), Point(10, 20)) << endl;
return 0;
}
模板没有被使用时,是不会生成函数实现;
模板的声明和实现如果分离到.h和.cpp中(cpp文件是单独编译的,模板单独编译的情况下,不会生成函数实现),会导致连接错误(寻找真正的函数地址的时候找不到),所以要都放在头文件里面(.hpp)
//头文件
template T add(T a,T b) {
return a + b;
}
//main文件
#include
#include "add.h"
using namespace std;
class Point {
friend ostream& operator<<(ostream&, const Point&);
int m_x;
int m_y;
public:
Point(int x,int y):m_x(x),m_y(y){}
const Point operator+(const Point &point) {
return Point(m_x+point.m_x,m_y+point.m_y);
}
};
ostream &operator<<(ostream &cout, const Point& point) {
cout << "("<(10, 20);
cout << add(10, 20) << endl;
cout << add(Point(10, 20), Point(10, 20)) << endl;
cout << add(10, 20) << endl;
cout << add(Point(10, 20), Point(10, 20)) << endl;
return 0;
}
没有使用类模板之前:
#include
using namespace std;
class Array {
int *m_data;
int m_size = 0;
int m_capacity;
public:
Array(int capacity) {
m_capacity = (capacity > 0) ? capacity : 10;
m_data = new int[m_capacity];
}
~Array() {
if (m_data == NULL) return;
delete[] m_data;
}
int getSize() {
return m_size;
}
void add(int value) {
if (m_size == m_capacity) {
m_capacity = m_capacity*2;
//扩容
int* temp_data = new int[m_capacity];
for (int i = 0; i < m_size;i++) {
temp_data[i] = m_data[i];
}
delete[] m_data;
m_data = temp_data;
}
m_data[m_size++] = value;
}
//获取当前索引
int get(int index) {
if (index<0 || index >= m_size) return 0;//抛出异常
return m_data[index];
}
int operator[](int index) {
return get(index);
}
};
int main()
{
Array data(5);
data.add(10);
data.add(2);
data.add(1);
data.add(3);
data.add(4);
data.add(15);
cout << data.getSize() << endl;
cout << data[2]<< endl;
cout << data[10]<< endl;
return 0;
}
使用之后:
#include
using namespace std;
template
class Array {
Item*m_data;
int m_size;
int m_capacity;
public:
//初始化数组 申请数组空间大小
Array(int capacity) :m_size(0){
m_capacity = (capacity > 0) ? capacity : 10;
m_data = new Item[m_capacity];
}
~Array() {
if (m_data == NULL) return;
delete[] m_data;
}
int getSize() {
return m_size;
}
void add(Item value) {
if (m_size == m_capacity) {
m_capacity = m_capacity*2;
//扩容
Item* temp_data = new Item[m_capacity];
for (int i = 0; i < m_size;i++) {
temp_data[i] = m_data[i];
}
delete[] m_data;
m_data = temp_data;
}
m_data[m_size++] = value;
}
//获取当前索引
Item get(int index) {
if (index<0 || index >= m_size) return 0;//抛出异常
return m_data[index];
}
Item operator[](int index) {
return get(index);
}
};
int main()
{
Array data(5);
data.add(10);
data.add(2);
data.add(1);
data.add(3);
data.add(4);
data.add(15);
cout << data.getSize() << endl;
cout << data[2]<< endl;
cout << data[10]<< endl;
return 0;
}
万能指针 void *
#include
#include "../40-友元/40-友元.cpp"
using namespace std;
template
class Array {
Item*m_data;
int m_size;
int m_capacity;
public:
//初始化数组 申请数组空间大小
Array(int capacity = 0) :m_size(0){
m_capacity = (capacity > 0) ? capacity : 10;
m_data = new Item[m_capacity];
}
~Array() {
if (m_data == NULL) return;
delete[] m_data;
}
int getSize() {
return m_size;
}
void add(Item value) {
if (m_size == m_capacity) {
m_capacity = m_capacity*2;
//扩容
Item* temp_data = new Item[m_capacity];
for (int i = 0; i < m_size;i++) {
temp_data[i] = m_data[i];
}
delete[] m_data;
m_data = temp_data;
}
m_data[m_size++] = value;
}
//获取当前索引
Item get(int index) {
if (index<0 || index >= m_size) return 0;//抛出异常
return m_data[index];
}
Item operator[](int index) {
return get(index);
}
};
int main()
{
Array data(5);
Arrayarray;//void * 万能指针 万能数组
array.add(&Point(2,3));
array.add(new Point(2,3));
data.add(10);
data.add(2);
data.add(1);
data.add(3);
data.add(4);
data.add(15);
cout << data.getSize() << endl;
cout << data[2]<< endl;
cout << data[10]<< endl;
return 0;
}
#pragma once
#include
using namespace std;
template
class Array {
friend ostream& operator<< <>(ostream& , const Array- & );
Item* m_data;
int m_size;
int m_capacity;
public:
//初始化数组 申请数组空间大小
Array(int capacity = 0);//默认参数只能写在声明中
~Array();
int getSize();
void add(Item value);
void remove(int index);
Item get(int index)const;
//重载[]
Item operator[](int index);// 访问array[0]
};
template
Array- ::Array(int capacity) :m_size(0) {
m_capacity = (capacity > 0) ? capacity : 10;
m_data = new Item[m_capacity];//默认的数组创建方式
}
template
Array- ::~Array() {
if (m_data == NULL) return;
delete[] m_data;
}
template
int Array- ::getSize() {
return m_size;
}
template
void Array- ::add(Item value) {
if (m_size == m_capacity) {
m_capacity = m_capacity * 2;
//扩容
Item* temp_data = new Item[m_capacity];
for (int i = 0; i < m_size; i++) {
temp_data[i] = m_data[i];
}
delete[] m_data;
m_data = temp_data;
}
m_data[m_size++] = value;
}
template
void Array- ::remove(int index) {
if (index < 0 || index >= m_size) return;//越界异常--抛出异常
if (index != m_size - 1) {
for (int i = index; i < m_size; i++) {
m_data[i] = m_data[i + 1];
}
}
m_size--;
}
template
Item Array- ::get(int index) const{
if (index < 0 || index >= m_size) return;//抛出越界异常
return m_data[index];
}
template
Item Array- ::operator[](int index) {
return get(index);
}
template
ostream & operator<< (ostream & cout, const Array- &array) {
cout << "[";
for (int i = 0; i < array.m_size;i++) {
if (i != 0) cout << ",";
cout << array.m_data[i];
}
return cout << "]";
}
//main.cpp
#include
#include "Array.hpp"
using namespace std;
class Point {
private:
friend ostream& operator<< (ostream&, const Point&);
int m_x;
int m_y;
public:
Point(int x = 0, int y = 0) :m_x(x), m_y(y) {};
};
ostream & operator<< (ostream& cout, const Point& point) {
cout << "(" << point.m_x << "," << point.m_y << ")";
return cout;
}
int main()
{
Array array;//void * 万能指针 万能数组
array.add(Point(1,2));
array.add(Point(2,2));
array.add(Point(3,2));
array.add(Point(4,2));
array.remove(2);
array.remove(7);
cout << array << endl;
return 0;
}
实现插入功能
/*
Array.hpp
...
*/
void insert(int index,const Item &array);
/*
...
*/
template
void Array- ::insert(int index, const Item& array) {
if (index < 0 || index >= m_size) return;//越界异常--抛出异常
m_size++;
if (m_size == m_capacity) {
m_capacity = m_capacity * 2;
//扩容
Item* temp_data = new Item[m_capacity];
for (int i = 0; i < m_size; i++) {
temp_data[i] = m_data[i];
}
delete[] m_data;
m_data = temp_data;
}
//如果不是在最后一个地方插入
if (index != m_size - 2) {
for (int i = m_size - 2; i > index; i--) {
m_data[i + 1] = m_data[i];
}
}
m_data[index] = array;
}
/*
...
*/
/*
main.cpp
...
*/
array.insert(1,Point(22,454));
/*
...
*/
类型转换的本质都是赋值,只是为了骗过编译器可以编译通过,本质都是赋值
//c语言风格的类型转换符
//类型 表达式
(type )expression
//
type(expression)
//隐式转换
int a = 10 ;
double b = a;
//c++中有四个类型转换符
static_cast;//
dynamic_cast;//
reinterpret_cast;//
const_cast;//去除const属性
xxx_cast(expression);
int a = 10;
用于去除const属性
const_cast(a);
const Person* p1 = new Person();
//C++语言风格
Person *p2 = const_cast(p1);
//C语言风格
Person *p2 = (Person *)p1;
用于多态类型的转换
class Person{};
class Student:Person{};
class Car{};
Person *p1 = new Person();
Person *p1 = new Student();
Car *car = dynamic_cast(p1);//可以交叉转换
//C++会做安全检测、C 语言不会做安全检测--纯粹的赋值操作
Student *stu1 = dynamic_cast(p1);
//相当于 Student *stu1 = new Person(); 子类指针指向父类对象 不安全
//Student *stu1 = NULL;直接变成空指针
Student *stu2 = dynamic_cast(p2);
//相当于 Student *stu1 = new Student(); 安全
对比dynamic_cast,缺乏运行时安全检测
不能交叉转换(不是同一继承体系的,无法转换),没有任何联系的进行转换
常用于基本数据类型的转换、非const转成const,和const_cast反过来
class Person{};
class Student:Person{};
class Car{};
Person *p1 = new Person();
const Person *p2 = p1;//隐式转换 非const转成const
const Person *p2 = static_cast(p1);//非const转成const
Person *p1 = new Student();
int a = 10;
double b = static_cast(a);
//不会做安全检测
Student *stu1 = static_cast(p1);
Student *stu2 = static_cast(p2);
Car *car = dynamic_cast(p1);//能交叉转换
Car *car = static_cast(p1);//不能交叉转换
属于比较底层的强制转换、没有任何类型检查和格式转换,仅仅是简单的二进制数据拷贝
class Person{};
class Student:Person{};
class Car{};
//小端模式
//0000 1010 0000 0000 0000 0000 0000 0000 二进制 4个字节
//0A 00 00 00 十六进制 4个字节
int a = 10;
//0A 00 24 40
double b= a;
//0A 00 00 00 CC CC CC CC
double b= reinterpret_cast(a);
cout<<"a="<(0x1100);
int *f= reinterpret_cast(d);
cout<<" d="<int a = 19;
decltype(a) b = 10;//获取a 的类型
//nullptr: 空指针 ,解决NULL二义性的问题
void func(int v){}
void func(int *v){}
func(NUll);//调用 int v C++98会产生二义性
int *p = (int *) 10;
func(NUll);//调用 int *v
func(nullptr);//调用 int *v
NUll是整数 0
nullptr是空指针 00000000
/*清空指针最新的方式*/
int *p = new int ;
delete p;
p = nullptr;
int array[] = {1,2,3,4,5,6,7};
for(int item : array){
cout<
/*
完整结构:
[capture list](params list) mutable exception -> return type{function body}
capture list :捕获外部变量列表
params list :形参列表,不能使用默认参数。不能使用省略参数名
multabe:用来说明是否可以修改捕获的变量
exception:异常设定
return type:返回值类型
function body:函数体
有时可以省略部分结构:
[capture list](params list) -> return type{function body}
[capture list](params list){function body}
[capture list]{function body}
*/
void func(){
cout<<"func()"<int{
cout<<"func()"<int{
return a+b;
}
}
#include
using namespace std;
int exec(int v1,int v2,int (* func)(int,int)) {
return func(v1,v2);
}
int main()
{
cout << exec(2, 3, [](int v1, int v2) {return v1 + v2; }) << endl;
cout << exec(2, 3, [](int v1, int v2) {return v1 - v2; }) << endl;
cout << exec(2, 3, [](int v1, int v2) {return v1 * v2; }) << endl;
cout << exec(2, 3, [](int v1, int v2) {return v1 / v2; }) << endl;
return;
}
//捕获
#include
using namespace std;
int exec(int v1,int v2,int (* func)(int,int)) {
return func(v1,v2);
}
int main()
{
//全局变量不分 值捕获和地址捕获
int a = 10;
int b = 10;
//默认都是值捕获 不可以操作参数
auto func1 = [a, b] {
cout << a << endl;
};
//地址捕获 可以操作
auto func2 = [&a,& b] {
a++;
b++;
cout << a << endl;
};
//地址捕获
auto func3 = [&] {
cout << a << endl;
};
//a是隐式捕获,其余都是地址捕获
auto func4 = [&,a] {
cout << a << endl;
};
//默认都是值捕获
/*auto func1 = [a] {
a++; 报错
};*/
auto func5 = [&a] {
a++;
};
/*可以修改,但是不是修改的外边的*/
auto func6 = [a]() mutable {
/*
int a; 相当于这里重新定义了一个a
*/
a++;
cout << a << endl;
};
auto func1 = [auto a,auto b] {
cout << a << endl;
};
//a = 20;
/*func1();
func2();*/
func6();
cout << a << endl;
return 0;
}
//泛型lambda表达式 C++14版本
auto func1 = [](auto v1, auto v2) {return v1 + v2;};
cout << func1(10,10.23) << endl;
//仅仅对捕获变量进行初始化
int a;
auto func2 = [a = 10](){
cout<
//作用域 C++17版本
int main()
{
//变量a,b作用域是它所在的if语句。以及其后边的if-else语句
if (int a = 10 ; a<=10) {
a = 1;
}
else if (int b = 20; a > 10) {
b = 2;
a = 2;
}
return 0;
}
语法错误
逻辑错误
异常:程序运行中可能会发生的错误(内存不够)
异常没有人处理可能会终止程序
#include
using namespace std;
int main()
{
cout << 1 << endl;
for (int i = 0; i < 10000; i++) {
//这句代码可能会产生异常
try {
int* p = new int[99999];
}
catch (...) { //... 捕获所有类型异常
cout << "产生异常了:内存不够" << endl;
break;
}
}
cout << 2 << endl;
return 0;
}
//无法捕获异常的情况
int main()
{
cout << 1 << endl;
int a = 0;
int b = 10;
try
{
int c = divide(b,a);
}
catch (...)
{
cout << "产生异常了" << endl;
}
cout << 2 << endl;
return 0;
}
//主动抛出异常
int divide(int v1,int v2) {
if (v2 == 0) {
//抛出异常
throw 999;
}
return v1 / v2; //系统无法捕获除法异常
}
int main()
{
cout << 1 << endl;
int a = 0;
int b = 10;
try
{
int c = divide(b,a);
}
catch (int exception)//int exception
{
cout << "产生异常了" << exception << endl;
}
cout << 2 << endl;
return 0;
}
//catch可以写多个
int divide(int v1,int v2) {
if (v2 == 0) {
//抛出异常
throw "不能除以0";
}
return v1 / v2; //系统无法捕获除法异常
}
int main()
{
cout << 1 << endl;
int a = 0;
int b = 10;
try
{
int c = divide(b,a);
}//catch可以写多个,类似于重载
catch (const char *exception)
{
cout << "产生异常了,const char *" << exception << endl;
}
catch (int exception)
{
cout << "产生异常了,int " << exception << endl;
}
cout << 2 << endl;
return 0;
}
throw异常的规则
//throw异常后,会在当前函数中查找匹配的catch,找不到就终止当前函数代码,去一层函数去查找,如果最终都查不到匹配的catch,就终止整个程序
void func1() {
cout<<"func1-begin"<
//在当前函数解决异常
void func1() {
cout << "func1-begin" << endl;
try
{
throw 999;
}
catch (int exception)
{
cout << "产生异常了" << endl;
}
cout << "func1-end" << endl;
}
void func2() {
cout << "func1-begin" << endl;
func1();
cout << "func1-end" << endl;
}
int main()
{
func2();
return 0;
}
//在上一级函数解决异常
void func2() {
cout << "func1-begin" << endl;
try
{
func1();
}
catch (int exception)
{
cout << "产生异常了" << endl;
}
cout << "func1-end" << endl;
}
//在最后的函数解决异常
int main()
{
try
{
func2();
}
catch (int exception)
{
cout << "产生异常了" << endl;
}
return 0;
}
int divide(int ,int) throw(int);//抛出int类型异常
void func2();//抛出任何异常
void func1() throw();//不抛出任何异常
void func3() throw(int,double);//只抛出int double类型异常
//这种自定义的没有意义
int divide(int v1,int v2) {
if (v2 == 0) {
//抛出异常
throw "不能除以0";
}
return v1 / v2; //系统无法捕获除法异常
}
int main()
{
cout << 1 << endl;
int a = 0;
int b = 10;
try
{
int c = divide(b,a);
}
catch (const char *exception)
{
cout << "产生异常了," << exception << endl;
}
cout << 2 << endl;
return 0;
}
有意义的异常
#include
using namespace std;
//int divide(int, int) throw (Exception);
class Exception {
public:
//接收子类异常类型 和 异常code
Exception(const char* temp,int code) {
cout<<"抛出" << temp <<"异常,异常code"<
需要手动管理内存
容易发生内存泄漏(忘记释放,出现异常)
释放之后产生野指针
//内存泄漏
void test(){
throw 999;
}
int main(){
try{
int *p = new int();
test();//出现异常
//这句代码无效
delete p;
}catch(...){
}
int *q = new int();
delete q;
q->//野指针
return 0;
}
为了解决传统指针存在的问题
auto_ptr:属于C++98标准,在C++11中已经不推荐使用,有缺陷,比如不能使用数组
shared_pty:属于C++11标准
unique_pty:属于C++11标准
auto_ptr p(new Person[]);//报错 不支持数组
#include
using namespace std;
class Person {
int m_age;
public:
Person(){
cout<<"Person()"< p(new Person(10));
//不支持隐式构造
//auto_ptr p = new Person();
p->run();
{
//报错的原因是:double free
Person person(29);
//直接报错
auto_ptr p1(person);
//原因 是指针指向的栈空间的对象,
}//在这里执行两次析构函数:Person person(29); 和 p1
//当调用p1析构函数的时候,发现指向person的指针已经销毁,就会报错;
return 0;
}
#include
using namespace std;
class Person {
int m_age;
public:
Person(){
cout<<"Person()"<
class SmartPointer {
private:
T* m_obj;
public:
SmartPointer(T *obj):m_obj(obj) {}
~SmartPointer() {
if (m_obj == nullptr) return;
delete m_obj;
m_obj = nullptr;
}
T *operator->() {
return m_obj;
}
};
int main() {
SmartPointer p(new Person(20));
p->run();
return 0;
}
:对一个对象产生强引用(strong reference)
每一个对象都有与之对应的强引用计数,记录着当前对象被多少个shared_ptr强引用着
当有一个新的shared_ptr指向对象时,对象的强引用计数就会+1
当有一个shared_pty销毁时(比如作用域结束),对象的强引用计数就会-1
当一个对象的强引用计数为0时(没有任何shared_ptr指向对象时),对象就会自动销毁(析构)
cout << "1" << endl;
{
shared_ptr p5;
{
//shared_ptr p(new Person[5]());
shared_ptr p1(new Person(10));
cout< p2 = p1;
cout << p1.use_count() << endl;
shared_ptr p3 = p1;
cout << p1.use_count() << endl;
shared_ptr p4 = p1;
cout << p1.use_count() << endl;
p5 = p4;//只有当最后一个智能指针销毁的时候,new Person才会销毁
cout << p1.use_count() << endl;
}
cout << p5.use_count() << endl;
cout << "2" << endl;
}
cout << "3" << endl;
//double free
Person* p = new Person();
{
shared_ptr p1(p);
}//~Person()
{
shared_ptr p2(p);
}//~Person()
#include
using namespace std;
class Person;
class Car {
public:
shared_ptr m_person = nullptr;
Car() {
cout << "Car()" << endl;
}
~Car() {
cout << "~Car()" << endl;
}
};
class Person {
public:
shared_ptr m_car = nullptr;
Person() {
cout << "Person()" << endl;
}
~Person() {
cout << "~Person()" << endl;
}
};
int main() {
{
shared_ptr person(new Person);
shared_ptr car(new Car);
person->m_car = car;//
car->m_person = person;//
}
//new Person 和new Car 不会被释放,原因就是强引用导致的 循环引用
//强引用计数器不为零,就不会调用析构
return 0;
}
#include
using namespace std;
class Person;
class Car {
public:
weak_ptr m_person;
Car() {
cout << "Car()" << endl;
}
~Car() {
cout << "~Car()" << endl;
}
};
class Person {
public:
shared_ptr m_car = nullptr;
Person() {
cout << "Person()" << endl;
}
~Person() {
cout << "~Person()" << endl;
}
};
int main() {
{
shared_ptr person(new Person);
shared_ptr car(new Car);
person->m_car = car;
car->m_person = person;
}
return 0;
}
同一时间只能指向一个对象
unique_ptr p0;
{
unique_ptr p1();
p0 = std::move(p1);//可以转移指向
}
64bit:以R开头
通用寄存器:RAX\RBX\RCX\RDX
一个寄存器存8个字节
32bit:以E开头
通用寄存器:EAX\EBX\ECX\EDX
16bit
通用寄存器:AX\BX\CX\DX
mov dest,src
//将src的内容赋值给dest,类似于dest = src
[地址值] h是十六进制
//中括号[]里面放的都是内存地址
int a =3;
//将3放到[ebp-8]内存地址所对应的空间
mov word ptr [ebp-8],3
//对应的汇编指令 word/2个字节 ptr/指定单位大小
mov eax,dword ptr [ebp-8] //从这个内存地址开始取 4个字节 放到eax中
// dword/4个字节 double word
//qword/8个字节 quad word
call [内存地址] //调用函数
jmp [内存地址] //跳转地址
lea eax,[1122h] //把地址值 给 eax 装载地址值
//相当于 eax == 1122h
ret //返回
rep //重复执行 这段代码
xor op1,op2//将op1和op2异或的结果赋值给op1,类似于op1 = op1^op2
add op1,op2//op1 = op1+op2
sub op1,op2//op1 = op1-op2
inc op1//op1 = op1+1 自加
dec op1//op1 = op1-1 自减
cmp eax,op2//比较 eax和op2的值 是否相等
jne //jump not equal 不相等的时候才跳转
[ebp-8]// ebp 栈空间地址
每个内存地址对应一个内存字节
一个变量的地址值,是它所有字节地址中的最小值
内存大小表示:一个字节 = 两个16进制 = 8个二进制 : 8 = 08 = 00000100
大小端:小段:高高低低,高字节放高地址,低字节放低地址
int a = 10;
__asm{
//汇编代码
mov eax,a
mov ax,10
mov eax,11223344h
}
//两个十六进制 是一个字节
001F4780 8B F4 mov esi,esp
001F4782 68 3C 10 1F 00 push 1F103Ch
(地址1)001F4780 8B F4 (2个字节) mov esi,esp
(地址2)001F4782 68 3C 10 1F 00(5个字节) push 1F103Ch
地址1和地址2正好相差2个字节
013447BA 6A 0A push 0Ah
013447BC E8 22 CC FF FF call display (013413E3h)//函数地址
display://在这里
013413E3 E9 28 06 00 00 jmp//跳转这个地址 01341A10
01341A10 55 push ebp //转到了这里
01341A11 8B EC mov ebp,esp
名称 | 地址 |
---|---|
IDA Pro | http://www.idapro.com.cn/ |
LordPE | |
PEID | |
吾爱破解OD | |
PETool | |
WinHex | |
汇编金手指 | |
ResourceHacker | |
OD | |
AsmFun | |
Cheat Engine | |
CodeinEX | |
Dbgview |