一.c和c++区别
二.封装
1.结构体参数的传递
编译结果和调用函数的push不一样
因为编译器在编译的时候不确定结构体有几个属性,所以就分配了一块内存空间、
但是不推荐用结构体作为参数,会浪费大量空间
可以使用指针代替结构体
2.封装的定义
把函数体定义在结构体里边就叫做封装
上面就是C
下面就是c++ 一模一样的效果
而这样的类似结构体的东西在c++中就叫做类
汇编代码没有任何区别,调用plus函数的时候虽然没有传参,但是编译器自动识别申明里边的操作。
3.总结
3.1封装
将函数定义到结构体内部,就是封装。
3.2类
带有函数的结构体,称为类
3.3成员函数
结构体里面的函数,成为成员函数
三.this指针
1.this指针定义
在这里便ecx里边的数值就是this指针
也就是这个结构体的首地址
总结:
2.this指针的使用
如果我使用的是
a = a;
b = b; 汇编代码会是
也就是a没有赋值给结构体里边的a
这是使用this的汇编
3.this指针运算
this不支持运算。
4.总结
四.构造函数与析构函数
1.构造函数
1.1定义
根据汇编代码发现,我们实例化对象的时候会调用Sclass这个构造函数
构造函数一般用来做初始化的操作
1.2总结
2.析构函数
2.1定义
~Person就是析构函数
析构函数只允许有一个
他是在实例化对象被销毁的时候执行
汇编代码
2.2总结
2.3析构函数何时执行
2.3.1当对象在堆栈中分配
那就是在return执行之前会执行
2.3.2当对象在全局区中分配
那么在程序结束前会执行析构函数
2.4析构函数的作用:用于清理工作
malloc是在堆中分配空间,free是释放空间
五.继承
1.定义
2. 父类成员和子类成员相同
例如:
1 2 3 4 5 6 7 8 9 10 11
| struct Person { int age; int sex; };
struct Teacher:Person { int age; int classId; };
|
那Teacher中有几个成员变量呢,还是4个
只不过编译器只能识别三个,如果要用Person中的age的话
得这样使用
汇编代码
3.继承不仅仅局限于父类
汇编代码
可以看出,它是有六个成员
4.多重继承
5.总结
六.类成员的访问控制
1.public与private的使用
2.private真的不能访问吗?
总结:
3.class与struct的区别
不能正常编译,但把class换成struct的话就能正常编译
把class中的x和y换成public也能正常编译
由此可见 class中的成员默认为private而struct中的成员为public
4.继承中的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class A { public: int a; int b; }; class B:A { public: int c; int d; }; int main(int argc, char* argv[]) { B b; b.a = 1; b.b = 2; b.c = 3; b.d = 4; }
|
这样是无法正常编译的 把b.a 和b.b去掉才能正常编译,也就是子类无法访问父类
因为继承默认是私有继承即
所以要改成
这样就能正常访问A中的成员了
5.private成员是否被继承

总结
七、在堆中创建对象
1.创建对象的几种方式
2.new delete的本质
2.1new
2.2delete
分析delete的执行流程:
delete _free_dbg
总结:
delete = free+析构函数
3.new[]/delete[]
3.1new[]
new创建对象每个对象都会调用构造函数
而malloc不会调用构造函数
3.2delete和delete[]区别
如果是Person* p = new Person[10]
delete只会删除第一个对象
而delete[]则会删除10个
八、引用类型
1.引用类型的定义
引用类型其实就是给变量取个别名,一切对应用类型的操作都会作用与变量
1 2 3 4
| int x = 1; int& p = x; p = 2; printf("%d \n",x);
|
输出结果为2
2.引用类型的本质
应用类型:
指针:
根据汇编代码我们可以看出,应用类型本质就是指针
3.应用类型与指针的区别
3.1赋值
可以看出,指针赋值修改的是指针,引用类型赋值修改的是对应的x
3.2运算
可以看出指针运算是加自己取值后的宽度
而引用类型是x加1就是对变量本身运算
也就是说指针运算是修改指针本身,而引用运算是修改变量
3.3总结
4.常引用
这样可能会由于指针指向错误的位置
所以修改成这样会让ref固定无法修改但是能修改ref里边成员的值
例如ref.x还是能修改的。如果想要完全不能修改就需要
const就叫做常引用,一般用于只读不可写。
九、面向对象程序设计之继承与封装
1.继承
1.1隐藏代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include<stdio.h> #include <iostream> using namespace std; class People { public: int age; int sex; }; class Teacher :public People { public: int level; }; int main() { Teacher t; t.age = -1; t.sex = 6; return 0; }
|
这种代码设计是合法但不合理的,因为按照现实年龄不能为负,但是这个程序又不会出错
所以按照一般的代码设计理念需要修改成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include<stdio.h> #include <iostream> using namespace std; class People { private: int age; int sex; public: void setAge(int a) { this->age = a; } void setSex(int s) { this->sex = s; } }; class Teacher :public People { private: int level; public: void setLevel(int l) { this->level = l; }; int main() { Teacher t; t.setLevel(1); t.setAge(1); return 0; }
|
这样更加符合,因为无法直接访问私有成员,只能通过我给出的方法操控,而我给出的方法就能够进行过滤筛选,例如参数只能大于0,就可以防止年龄为负数
1.2继承中的构造函数
父类没有构造函数,子类有构造函数的话,子类只会调用子类的构造函数。
子类和父类同时有构造函数的时候,子类默认调用子类的构造函数的同时会调用父类的无参构造函数,若父类没有无参构造函数,那么编译就会出错。
所以每当创建一个类时都提供一个无参的构造函数是一个好习惯。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include "main.h" #include<stdio.h> #include <iostream> using namespace std; class People { private: int age; int sex; public: People() {} People(int a, int s) { this->age = a; this->sex = s; } void setAge(int a) { this->age = a; } void setSex(int s) { this->sex = s; } }; class Teacher :public People { private: int level; public: Teacher() {} Teacher(int a, int s, int l) :People(a, s) { this->level = l; } void setLevel(int l) { this->level = l; }; }; int main() { Teacher t; t.setLevel(1); t.setAge(1); return 0; };
|
那如果是这种情况,我要调用父类的有参构造函数,就应该是这样的
1 2 3
| Teacher(int a, int s, int l) :People(a, s) { this->level = l; }
|
十、多态
1.代码的复用
面向对象的编程特点就是避免重复造轮子
如果我要定义两个函数
1 2 3 4 5 6 7 8
| void PrintPerson(Person& p) { p.Print(); } void PrintTeacher(Teacher& t) { t.Print(); }
|
那么他们有重复的功能,如何合并成一个呢
c++是支持用父类的指针指向子类的对象
但不支持用子类的指针指向父类的对象,例如
1 2 3 4
| Teacher t; Person* p = &t; Person p; Teacher* t = &p;
|
那么我们就可以把两个函数合并为同一个函数
1 2 3 4 5 6 7 8 9
| void Myprint(Person& p) { p.Print(); } int main() { Teacher t(1,2,3); Myprint(t); }
|
这样就会调用Teacher类中的print
1 2 3 4 5 6 7 8 9
| void Myprint(Person& p) { p.Print(); } int main() { Person t(); Myprint(t); }
|
这样就会调用Person里边的print
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include "main.h" #include<stdio.h> #include <iostream> using namespace std; class People { private: int age; int sex; public: People() {} People(int a, int s) { this->age = a; this->sex = s; } void setAge(int a) { this->age = a; } void setSex(int s) { this->sex = s; } virtual void print() { printf("age:%d,sex:%d\n", this->age, this->sex); }
}; class Teacher :public People { private: int level; public: Teacher() {} Teacher(int a, int s, int l) :People(a, s) { this->level = l; } void print() { People::print(); printf("level:%d\n", this->level); } void setLevel(int l) { this->level = l; }; }; int main() { Teacher t; t.setLevel(1); t.setAge(1); return 0; };
|
像这种子类和父类有同一个函数名的方法,就叫做函数的重写,如果是在同一个类呢就叫做重载。
2.多态定义
1 2 3 4
| void Myprint(Person& p) { p.Print(); }
|
像这种父类的指针有多种形态就叫做多态
传入父类对象就调用父类的print
传入子类对象就调用子类的print
而c++实现多态就是利用虚函数
1
| virtual void print() { printf("age:%d,sex:%d\n", this->age, this->sex); }
|
这个用virtual定义的是虚函数。
虚函数写父类
3.纯虚函数
如果父类函数并没有什么功能,但子类需要使用这个函数,那么就可以定义一个纯虚函数
virtual 返回类型 函数名(参数列表) = 0;
它的作用就是
十一、虚表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include "main.h" #include<stdio.h> #include <iostream> using namespace std; class A { public: void Test() { cout << "TestA" << endl; } }; class B : public A { public: void Test() { cout << "TestB" << endl; } }; void Fun(A* p) { p->Test(); } int main() { A a; B b; Fun(&a); Fun(&b); };
|
如果我们没定义虚函数,那么虽然传入的是子类的对象,但还是调用的父类的Test函数
输出结果
看会办代码可以发现,申明函数的时候就已经写死了是调用A的Test
那我们改成虚函数再看看
汇编:
00007FF78A7422D1 call qword ptr [rax]
这种就叫做间接调用
如果使用虚函数,那么编译器就会使用间接调用
1.带有虚函数的对象大小
1 2 3 4 5 6 7 8
| class A { public: int test; virtual void Test() { cout << "TestA" << endl; } };
|
我们看这个类的对象的大小
结果是16个字节
1 2 3 4 5 6 7 8
| class A { public: int test; void Test() { cout << "TestA" << endl; } };
|
结果是4个字节
可以看出来virtual函数会多12个字节
其实不是 其实是多8个,因为
1 2 3 4 5 6 7 8 9
| class A { public: int test; int test1; virtual void Test() { cout << "TestA" << endl; } };
|
这个也是16字节
只是因为字节对齐的原因所以结果是16个字节
虚函数多的八个字节就是虚表
2.虚表的位置
可以看出a对象多了一个成员_vfptr而这个里边存储了一个地址,这个地址存储了一张表,就是虚表,而在虚表中就有要调用的函数
_vfptr中存储的0x00007ff70eb8bcb8
就是虚表的地址
虚表中又有0x00007ff70eb81532
就是要执行的函数的地址
也就是A::Test
这是父类A
接下来看子类B的
看得出来虚表的地址是不一样的
3.虚表的内容
接下来我们就知道这个是啥意思了
这个就是先将对象p中的第一个成员的值(也就是虚表的地址)放入rax中,然后再将rax地址的值(也就是虚表中的第一个值,也就是要执行函数的地址放入rax中),接下来就是执行这个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include "main.h" #include<stdio.h> #include <iostream> using namespace std; class A { public: int test; int test2; virtual void Test() { cout << "TestA" << endl; } virtual void Test1() { cout << "TestB" << endl; } }; class B : public A { public: void Test() { cout << "TestB" << endl; } void Test1() { cout << "TestB" << endl; } }; void Fun(A* p) { p->Test(); } int main() { A a; B b; Fun(&a); Fun(&b); };
|
加了一个函数看看
现在虚表中有两个值了
总结:多态的实现原理就是调用虚函数的话,如果调用的是对象a则执行a中的虚表中的函数,如果是第一个函数那么就执行虚表中第一个函数,第二个的话就执行虚表中的第二个函数。
十二、运算符重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<stdio.h> #include <iostream> using namespace std; class number { private: int x; int y; public: number(int a, int b) { this->x = a; this->y = b; } bool Max(number& p) { return this->x > p.x && this->y > p.y; } }; int main() { number a(4,4),b(3,3); bool r = a.Max(b); printf("%d", r); return 0; }
|
我们编写了一个比较大小的方法,但这样调用实在很麻烦,我们就可以利用运算符重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include<stdio.h> #include <iostream> using namespace std; class number { private: int x; int y; public: number(int a, int b) { this->x = a; this->y = b; } bool operator>(number& p) { return this->x > p.x && this->y > p.y; } }; int main() { number a(4,4),b(3,3); bool r = a > b; printf("%d", r); return 0; }
|
也就是将
1 2 3
| bool Max(number& p) { return this->x > p.x && this->y > p.y; }
|
换成了
1 2 3
| bool operator>(number& p) { return this->x > p.x && this->y > p.y; }
|
运算的时候直接使用>就行了
这个就是运算符重载的本质就是给运算符和函数关联而已
十三、模板
1.模板的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> using namespace std; void Sort(int* arr, int nLength) { int temp=0; for(int i=0;i<nLength;i++){ for(int j=0;j<nLength-1-i;j++){ if(arr[j]>arr[j+1]){ temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } } } }
int main() { int arr[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; Sort(arr, 10); for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } system("pause"); return 0; }
|
这是一个简单的冒泡排序,也能正常输出结果。但是有个问题,这个函数只能传入int类型的数组,我要是别的类型就不行了怎么办,这就要用到模板
1 2 3 4 5 6 7 8 9 10 11 12 13
| template <class T> void Sort(T* arr, int nLength) { T temp=0; for(int i=0;i<nLength;i++){ for(int j=0;j<nLength-1-i;j++){ if(arr[j]>arr[j+1]){ temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; } } } }
|
那么我们接下来使用任何类型都可以运行了
所以模板中的T就相当于占位符
如果有多个要替换的就可以多加几个占位符
1
| template <class T,class M,class A>
|
都可以的
2.模板的本质
1 2 3 4 5
| char arr1[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; int arr2[10] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
Sort(arr1, 10); Sort(arr2, 10);
|
我们连续调用两次,看汇编代码到底是如何执行的
我们发现虽然执行的是同一个函数,但地址完全不同,所以他的本质就是编译器替我们编写了两个函数。
3.在类中使用模板
之前都是在函数中使用模板
在类中使用模板其实是差不多的
使用方法就是实例化对象时
这样int就会替代T char替代M
十四、纯虚函数
1.纯虚函数的定义
将成员函数声明为virtual
该函数没有函数体(后跟0)
如:
1 2 3 4 5
| class CBank { pubilc: virtual double GetAnnualRate() = 0; };
|
2.抽象类
含有纯虚函数的类,称为抽象类(Abstract class)
抽象类也可以包含普通的函数
抽象类不能实例化
3.抽象类的意义
抽象类可以看成是一个标准,任何该类的子类都必须遵守这个标准。
而遵守这个标准我们可以通过对父类指针操作进行遍历子类的操作,便于管理。
十五、对象拷贝
1.拷贝构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class CObject { private: int x; int y; public: CObject() { } CObject(int a, int b) { this->x = a; this->y = b; }
}; int main() { CObject x(1, 2); CObject y(x); system("pause"); return 0; }
|
从汇编代码也可以看出,实际上就是内存的拷贝,而且父类的所有成员都能拷贝。
2.拷贝构造函数的缺点
虽然拷贝构造函数有很多优点,但也有缺点,当有个成员是指针类型的时候,拷贝构造函数会完完全全拷贝下来,而不是复制指针的这块内存空间到其他内存空间,所以当被拷贝的对象被释放之后,拷贝后的对象的指针也会被释放
而这种被称为浅拷贝,如果能做到,重新申请一块内存并且把指针里的数值复制过来就成为深拷贝
深拷贝的实现方法
这个的参数只能是引用类型 不能是指针
3.总结(拷贝函数)
4.重载赋值运算符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <iostream> using namespace std; class Base { private: int x; int y; public: Base(int a, int b) { x = a; y = b; } void GetNumber() { cout << "x = " << x << endl; cout << "y = " << y << endl; } ~Base() { cout << "析构函数" << endl; }; }; int main() { Base a(1, 2); Base b(3, 4); a = b; a.GetNumber(); system("pause"); return 0; }
|
运算结果
可以看出c++是支持两个对象之间直接用=赋值的
那有父类的话还能拷贝吗,我们来试试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <iostream> using namespace std; class Base { private: int x; int y; public: Base(int a, int b) { x = a; y = b; } void GetNumber() { cout << "x = " << x << endl; cout << "y = " << y << endl; } ~Base() { cout << "析构函数" << endl; }; }; class Basex : public Base { private: int z; public: Basex(int a, int b, int c) : Base(a, b) { z = c; } void GetNumber() { Base::GetNumber(); cout << "z = " << z << endl; } ~Basex() { cout << "析构函数" << endl;; } }; int main() { Base a(1, 2); Base b(3, 4); a = b; a.GetNumber(); system("pause"); return 0; }
|
运算结果
也是可以拷贝下来的
=运算符和拷贝构造函数一样都属于浅拷贝
解决办法就是重载运算符
但重载运算符有个问题,就是如果有父类,则也要显性调用父类的重载运算符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <iostream> using namespace std; class Base { private: int length; char* num; public: Base() { this->length = 0; this->num = NULL; } Base(const char* num) { this->length = strlen(num)+1; this->num = new char[this->length]; memcpy(this->num, num, this->length); } Base& operator=(Base& b) { this->length = b.length; if (this->num != NULL) { delete[] this->num; } this->num = new char[this->length]; memcpy(this->num, b.num, this->length); return *this;
}
}; class Basex : public Base { private: int z; public: Basex() {} Basex(int z,const char* num) : Base(num) { this->z = z; } Basex& operator=(Basex& b) { Base::operator=(b); this->z = b.z; return *this; } }; int main() { Basex nice(10,"你好"); Basex nice2(20,"你好"); nice = nice2; system("pause"); return 0; }
|
执行结果没有问题
十六、友元
友元的存在破坏了面向对象的编程。不推荐使用,目前JAVA等编程语言已经取消了这个说法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> using namespace std; class B { private: int x; int y; public: B() { this->x = 0; this->y = 0; } }; void MyPrint(B& b) { printf("%d \n", b.x); } int main() { B b; MyPrint(b); system("pause"); return 0; }
|
正常来讲 MyPrint函数无法访问b.x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> using namespace std; class B { friend void MyPrint(B& b); private: int x; int y; public: B() { this->x = 0; this->y = 0; } }; void MyPrint(B& b) { printf("%d \n", b.x); } int main() { B b; MyPrint(b); system("pause"); return 0; }
|
但如果修改成这样 ,用friend修饰,就可以正常访问,这个就叫做友元,其实没有多大意义,不建议使用。感觉像是c++向面向过程的一种妥协。
友元也能修饰类。将friend void MyPrint(B& b);
改为friend class Test;
就行
十七、内部类
1.内部类的定义
类内部的类就叫做内部类。例如:
1 2 3 4 5 6 7 8 9
| class Outter { private: int x; public: class inner { private: int y; }; };
|
inner类就是内部类
2.内部类的属性
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Outter { private: int x; public: class inner { private: int y; public: inner() { Outter::x = 10; } }; };
|
这个代码会报错原因是
可以发现内部类无法访问外部类的私有成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Outter { private: int x; public: class Inner { private: int y; public: Inner() { Outter::x = 10; } }; Outter() { Inner i; i.y = 10; } };
|
报错信息
可以发现外部类也无法访问内部类私有成员
3.内部类的作用
内部类定义在私有成员里边便于自己使用,而不是放在public里边
如果一个类只在模块内部使用,则可以实现类名隐藏。
十八、命名空间
1.命名冲突问题
在不使用类之前,是没办法定义两个名字相同的函数的。但使用类的话,两个不同的类可以使用名字相同和函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> using namespace std; class B { public: void Fn() {
} }; class C { public: void Fn() {
} }; int main() { system("pause"); return 0; }
|
这样是没有问题的
但还是有个问题,当开发的人很多时,可能会使用相同的类名,那么就要用到所谓的命名空间(namespace)
2.命名空间的使用
1 2 3 4 5 6 7 8 9 10
| namespace 名称x{ 1.全局变量 2.函数 3.类 } namespace 名称y{ 1.全局变量 2.函数 3.类 }
|
x y中的一切变量都可以完全一样
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include<stdio.h> #include <iostream> using namespace std; namespace A { int x = 100; } namespace B { int x = 200; } int main() { printf("%d \n", A::x); printf("%d \n", B::x); system("pause"); return 0; }
|
使用类、函数也一样
但是这种申明一般都写在头文件
1 2 3 4 5 6 7
| namespace A { int x = 100; } namespace B { int x = 200; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include<stdio.h> #include <iostream> #include"main.h" using namespace std;
int main() { printf("%d \n", A::x); printf("%d \n", B::x); system("pause"); return 0; }
|
那一直要使用A::有点麻烦
可以直接使用using namespace,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include<stdio.h> #include <iostream> #include"main.h" using namespace A;
int main() { printf("%d \n", x); printf("%d \n", x); system("pause"); return 0; }
|
运行结果就会编程这样
那现在又有问题了,
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include<stdio.h> #include <iostream> #include"main.h" using namespace A;
int main() { int x = 200; printf("%d \n", x); printf("%d \n", x); system("pause"); return 0; }
|
这样的话x是哪个值呢
他会选择200这个值
那如果要使用全局命名空间的话就得使用::x
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include<stdio.h> #include <iostream> #include"main.h" using namespace A;
int main() { int x = 200; printf("%d \n", x); printf("%d \n", ::x); system("pause"); return 0; }
|
没有问题
十九、static关键词
1.面向过程设计的static作用
1.1私有的全局变量
我们来看两个有趣的现象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <iostream>
using namespace std; void Test(int flag) { char Buffer[0x10] = { 0 }; if (flag) { strcpy_s(Buffer,sizeof(Buffer), "你好"); } else { printf("%s \n",Buffer); }
} int main() { Test(1); Test(0); Test(0); Test(0); Test(0); Test(0);
system("pause"); return 0; }
|
这样输出结果为:
那我们在Buffer加上static
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <iostream>
using namespace std; void Test(int flag) { static char Buffer[0x10] = { 0 }; if (flag) { strcpy_s(Buffer,sizeof(Buffer), "你好"); } else { printf("%s \n",Buffer); }
} int main() { Test(1); Test(0); Test(0); Test(0); Test(0); Test(0);
system("pause"); return 0; }
|
输出结果
所以我们可以发现,被static修饰过后的变量会变成全局变量,只不过只能这个函数访问。被称为私有的全局变量x
那如果有多个cpp文件,例如A.CPP B.CPP
如果A.CPP中有static修饰过的函数,变量 就算B.CPP包含了A.H还是使用不了
因为static修饰过的变量只能在本文件使用
2.面向对象设计中的static之静态数据成员
3.面向对象设计中的static之静态成员函数
4.static关键词的应用:单子模式
有些时候我们希望定义的某些类只能有一个对象存在,如何进行限制。