冬季什么行业做网站比较多,中国住房和城乡建设部网站公文,住房与城乡建设部,聊城专业网站设计公司内存分区模型 C程序在执行时#xff0c;将内存大方向划分为4个区域
代码区#xff1a;存放函数体的二进制编码#xff0c;由操作系统进行管理全局区#xff1a;存放全局变量和静态变量以及常量栈区#xff1a;由编译器自动分配释放#xff0c;存放函数的参数值#xff…内存分区模型 C程序在执行时将内存大方向划分为4个区域
代码区存放函数体的二进制编码由操作系统进行管理全局区存放全局变量和静态变量以及常量栈区由编译器自动分配释放存放函数的参数值局部变量等堆区由程序员分配和释放若程序员不释放程序结束时由操作系统回收 内存4区意义
不同的区域存放不同的数据赋予不同的生命周期给我们更大的灵活编程
程序运行前 在程序编译后生成了exe可执行程序未执行该程序前分为两个区域 代码区 存放CPU执行的机器指令 代码区是共享的共享的目的是对于频繁被执行的程序只需要在内存中有一份代码即可 代码区是只读的使其只读的原因是防止程序意外的修改了它的指令 全局区 全局变量和静态变量存放在此 全局区还包括了常量区字符串常量和其他常量也存放在此 该区域的数据在程序结束之后由操作系统释放
示例
int g_a 10;
int g_b 10;const int c_g_a 10;
const int c_g_b 10;int main(){//局部变量不在全局区int a 10;int b 10;cout (int)a endl;cout (int)a endl;cout (int)g_a endl;cout (int)g_b endl;static int s_a 10;static int s_b 10;cout (int)s_a endl;cout (int)s_b endl;cout (int)hello world endl;cout (int)hello world1 endl;cout (int)c_g_a endl;cout (int)c_g_b endl;//局部常量不在全局区中const int c_l_a 10;const int c_l_b 10;cout (int)c_l_a endl;cout (int)c_l_b endl;system(pause);return 0;
}程序运行后 栈区 由编译器自动释放存放函数的参数值局部变量等 注意事项不要返回局部变量的地址栈区开辟的数据由编译器自动释放
示例
int * func(){int a 10;return a;
}int main(){int *p func();cout *p endl;cout *p endl;system(pause);return 0;
}注意 x64函数的调用通常使用寄存器来传递参数和返回值如果想要达到x86的效果可以在两次输出之前输入system(pause) 堆区 由程序员分配释放若程序员不能释放程序结束时由操作系统回收 在C中主要利用new在堆区开辟内存
示例
int* func() {int* a new int(10);return a;
}int main() {int* p func();cout *p endl;cout *p endl;system(pause);return 0;
}new操作符 C中利用new操作符在堆区开辟数据 堆区开辟的数由程序员手动开辟手动释放释放利用操作符delete 语法new 数据类型 利用new创建的数据会返回该数据对应的类型的指针
基本语法
int* func() {int* a new int(10);return a;
}int main() {int* p func();cout *p endl;cout *p endl;delete p;system(pause);return 0;
}示例
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;system(pause);return 0;
}引用
引用的基本使用 作用给变量起别名 语法数据类型 别名 原名
示例
int main(){int a 10;int b a;cout a endl;cout b endl;b 100;cout a endl;cout b endl;system(pause);return 0;
}引用注意事项
引用必须初始化引用在初始化后不可以改变
示例
int main(){int a 10;int b 20;//错误引用必须初始化//int c;//一旦初始化后就不可以改变int c a;//这是赋值操作不是更改引用 c b;cout a endl;cout b endl;cout c endl;system(pause);return 0;
}引用做函数参数 作用函数传参时可以利用引用的技术让形参修饰实参 优点可以简化指针修改实参
示例
void mySwap01(int a, int b) {int temp a;a b;b temp;
}void mySwap02(int* a, int* b) {int temp *a;*a *b;*b temp;
}void mySwap03(int a, int b) {int temp a;a b;b temp;
}int main() {int a 10;int b 20;mySwap01(a, b);cout a endl;cout b endl;mySwap02(a, b);cout a endl;cout b endl;mySwap03(a, b);cout a endl;cout b endl;system(pause);return 0;
}引用做函数返回值 作用引用是可以作为函数的返回值存在的 注意不要返回局部变量引用 用法函数调用作为左值
示例
int test01(){int a 10;return a;
}int test02(){static int a 20;return a;
}int main(){int ref test01();cout ref endl;cout ref endl;int ref2 test02();cout ref2 endl;cout ref2 endl;test02() 1000;cout ref2 endl;cout ref2 endl;system(pause);return 0;
}引用的本质 本质引用的本质在c内部实现是一个指针常量
示例
void func(int ref){ref 100;
}int main(){int a 10;int ref a;ref 20;cout a endl;cout ref endl;func(a);system(pause);return 0;
}常量引用 作用常量引用主要用来修饰形参防止误操作 在函数形参列表中可以加const修饰形参防止形参改变实参
示例
void showValue(const int v){//v 10;cout v endl;
}int main(){//引用本身需要一个合法的内存空间因此运行错误//int ref 10;//加入const就不会报错const int ref 10;cout ref endl;int a 10;showValue(a);system(pause);return 0;
}函数提高
函数默认参数 在C中函数的形参列表中的形参是可以由默认值的 语法返回值类型 函数名(参数 默认值){}
示例
int func(int a,int b 10,int c 10){return a b c;
}//如果某个位置参数有默认值那么从这个位置往后从左向右必须都要有默认值
//如果函数声明有默认值函数实现的时候就不能有默认参数int func2(int a 10,int b 10);
int func2(int a,int b){return a b;
}int main(){cout func(20,20) endl;cout func(100) endl;system(pause);return 0;
}函数占位参数 C中函数的形参列表里可以有占位参数用来做占位调用函数时必须填补该位置 语法返回值类型 函数名(数据类型){} 在现阶段函数的占位参数存在意义不大但是后面会用到该技术
示例
void func(int a,int){cout func endl;
}int main(){//占位符必须填补func(10,10);system(pause);return 0;
}函数重载
函数重载概述 作用函数名可以使用提高复用性 函数重载满足条件
同一个作用于下函数名称相同函数参数类型不同 或者 个数不同 或者 顺序不同 注意函数的返回值不可以作为函数重载的条件
示例
void func(){cout func endl;
}
void func(int a){cout int a endl;
}void func(double a){cout double a endl;
}void func(int a,double b){cout double b endl;
}void func(double a,int b){cout int b endl;
}//函数返回值不可以作为函数重载条件
/*int func(double a,int b){cout double a endl;
}*/int main(){func();func(10);func(3.14);func(10,3.14);func(3.14,10);system(pause);return 0;
}函数重载注意事项
引用作为重载条件函数重载碰到函数默认参数
示例
void func(int a){cout a endl;
}void func(const int a){cout const endl;
}void func2(int a,int b 10){cout a,b 10 endl;
}void func2(int a){cout a endl;
}int main(){int a 10;//调用无constfunc(a);//调用有constfunc(10);//碰到默认参数产生歧义需要避免//func2(10);system(pause);return 0;
}类和对象 C面向对象的三大特性为封装、继承、多态 C认为万事万物皆为对象对象上有其属性和行为 例如 人可以作为对象属性有姓名、年龄等行为有走、跑等 车作为对象属性有轮胎、方向盘等行为有载人、放音乐等 具有相同性质的对象可以抽象称为类
封装
封装的意义 封装是C面向对象三大特性之一 封装的意义 将属性和行为作为一个整体表现生活中的事物 将属性和行为加以权限控制 封装的意义一 在设计类的时候属性和行为写在一起表现事物 语法class 类名{访问权限:属性/行为};
示例设计一个圆类求圆的周长
const double pi 3.14;class Circle{
public:int m_r;double calculate(){return 2 * pi * m_r;}
};int main(){Circle c1;c1.m_r 10;cout c1.calculate() endl;system(pause);return 0;
}封装意义二 类在设计时可以把属性和行为放在不同的权限下加以控制 访问权限有三种 1. public 公共权限 2. protected 保护权限 3. private 私有权限
示例
class Person{
public:string m_Name;protected:string m_Car;private:int m_Password;public:void func(){m_Name 张三;m_Car 汽车;m_Password 123456;}
};int main(){Person p;p.m_Name 李四;//在类外访问不到//p.m_Car aodi;//在类外访问不到//p1.m_Password 123;p.func();system(pause);return 0;
}struct 和 class 区别 在 C 中 struct 和 class 唯一的区别就在于默认的访问权限不同 区别
struct 默认权限为公共class 默认权限为私有
示例
class C1{int m_A;
};struct C2{int m_A;
};int main(){C1 c1;c1.m_A 10;C2 c2;c2.m_A 10;system(pause);return 0;
}成员属性设置为私有 优点1将所有成员属性设置为私有可以自己控制读写权限 优点2对于写权限可以检测数据的有效性
示例
class Person{
public:void SetName(string name){m_Name name;}string GetName(){return m_Name;}int GetAge(){return m_Age;}void SetAge(int age){m_Age age;}void SetIdol(string idol){m_Idol idol;}private:string m_Name;int m_Age 18;string m_Idol;
};int main(){Person p;p.SetName(张三);cout p.GetName() endl;cout p.GetAge() endl;p.SetIdol(小明);system(pause);return 0;
}对象的初始化和清理 C中的每个对象都会有初始设置以及对象销毁前的清理数据的设置
构造函数和析构函数 对象的初始化和清理也是两个非常重要的安全问题 一个对象或变量没有初始状态对其使用后果是未知 同样的使用完一个对象或变量没有及时清理也会造成一定的安全问题 C利用了构造函数和析构函数解决上述问题这两个函数将会被连一起自动调用完成对象初始化和清理工作 对象的初始化和清理工作是编译器强制要求做的事情因此如果不提供构造和析构编译器会提供编译器提供的构造函数和析构函数是空实现 构造函数主要作用在于创建对象时为对象的成员属性赋值构造函数由编译器自动调用无需手动调用 析构函数主要作用在于对象销毁前系统自动调用执行一些工作 构造函数语法类名(){} 构造函数没有返回值也不写void 函数名称和类名相同 构造函数可以有参数因此可以发生重载 程序在调用对象时候会自动调用构造无需手动调用而且只会调用一次 析构函数语法~类名(){} 析构函数没有返回值也不写void 函数名称与类名相同在名称前加上符号~ 析构函数不可以有参数因此不可以发生重载 程序在对象销毁前会自动调用析构无需手动调用而且只会调用一次
示例
class Person{
public:Person(){cout 构造函数 endl;}~Person(){cout 析构函数 endl;}
};void Test(){Person p;
}int main(){Test();system(pause);return 0;
}构造函数的分类和调用 两种分类方式 按参数分为有参构造和无参构造 按类型分为普通构造和拷贝构造 三种调用方式 括号法 显示法 隐式转换发
示例
class Person{
public://无参默认构造函数Person(){cout 无参构造函数 endl;}//有参构造函数Person(int a){age a;cout 有参构造函数 endl;}//拷贝构造函数Person(const Person p){age p.age;cout 拷贝构造函数 endl;}//析构函数~Person(){cout 析构函数 endl;}public:int age;
};void test01(){Person p;
}void test02(){//括号法常用Person p1(10);//注意调用无参构造函数不能加括号如果加了编译器认为是一个函数声明//Person p2();//显式法Person p2 Person(10);Person p3 Person(p2);//单独写就是匿名对象当前行结束马上析构//Person(10);//隐式转换法Person p4 10;Person p5 p4;//注意不能利用拷贝构造函数初始化匿名对象编译器认为是对象声明//Person p5(p4);
}int main(){test01();test02();system(pause);return 0;
}拷贝构造函数调用时机 C中拷贝构造函数调用时机通常有三种情况 使用一个已经创建完毕的对象来初始化一个新对象 值传递的方式给函数参数传递 以值方式返回局部对象
示例
class Person{
public:Person(){cout 默认构造函数 endl;mAge 0;}Person(int age){cout 有参构造函数 endl;mAge age;}Person(const Person p){cout 拷贝构造函数 endl;mAge p.mAge;}~Person(){cout 析构函数 endl;}int mAge;
};void test01(){Person p1(20);Person p2(p1);cout p2.mAge endl;
}void doWork(Person p){}void test02(){Person p;doWork(p);
}Person doWork2(){Person p1;return p1;
}void test03(){Person p doWork2();
}int main(){test01();test02();test03();system(pause);return 0;}构造函数调用规则 默认情况下C编译器至少给一个类添加3个函数 默认构造函数无参函数体为空 默认析构函数无参函数体为空 默认拷贝构造函数对属性值进行拷贝 构造函数调用规则如下 如果用户定义有参构造函数C不在提供默认无参构造但是会提供默认拷贝函数 如果用户定义拷贝构造函数C不会再提供其他构造函数
示例
class Person{
public:Person(){cout 默认构造函数 endl;}Person(int a){age a;cout 有参构造函数 endl;}Person(Person p){m_Age p.m_Age;cout 拷贝构造函数 endl;}~Person(){cout 析构函数 endl;}int m_Age;
};void test01(){Person p;p.m_Age 18;Person p2(p);cout p2.m_Age endl;
}void test02(){Person p;Person p2(18);Person p3(p2);cout p3.m_Age endl;
}int main(){test01();test02();system(pause);return 0;
}深拷贝与浅拷贝 浅拷贝简单的赋值拷贝操作 深拷贝在堆区重新申请空间、进行拷贝工作
class Person{
public:Person(){cout 无参构造函数 endl;}Person(int age,int height){cout 有参构造函数 endl;m_Age age;m_hight new int(height);}Person(const Person p){cout 拷贝构造函数 endl;m_Age p.m_Age;//深拷贝操作m_height new int(*p.m_height);}~Person(){cout 析构函数 endl;if(m_height ! NULL){delete m_height;}}public:int age;int* m_height;
};void test01(){Person p1(10,100);Person p2(p1);cout p1.m_Age *p1.m_height endl;cout p2.m_Age *p2.m_height endl;
}int main(){test01();system(pause);return 0;
}如果属性在堆区开辟一定要自己提供拷贝构造函数防止浅拷贝带来的问题堆区重复释放
初始化列表 作用 C提供了初始化列表语法用来初始化属性 语法构造函数():属性1(值1),属性2(值2)...{}
示例
class Person{
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){}int m_A;int m_B;int m_C;
};void test01(){//Person p(10,20,30);Person p(30,20,10);cout p.m_A endl;cout p.m_B endl;cout p.m_C endl;
}int main(){test01();system(pause);return 0;
}类对象作为类成员 C中的类可以是另一个类的对象将该对象称为对象成员
例如
class A{}
class B{A a;
}B类中有对象A作为成员A为对象成员
示例
class Phone {
public:Phone(string pName) {cout phone endl;m_PName pName;}~Phone() {cout phone ~ endl;}string m_PName;
};class Person {
public:Person(string name, string pName) :m_Name(name), phone(pName){cout Person endl;}~Person() {cout Person ~ endl;}string m_Name;Phone phone;
};void test01() {Person p(张三, Apple);cout p.m_Name endl;cout p.phone.m_PName endl;
}int main() {test01();system(pause);return 0;
}总结 当其他类对象作为本类成员构造时候先构造类对象再构造自身 析构的顺序与构造的相反
静态成员 静态成员就是在成员变量和成员函数前加上关键字static称为静态成员 静态成员分为 静态成员变量 所有对象共享的一份数据 在编译阶段分配内存 类内生命类外初始化 静态成员函数 所有对象共享同一个函数 静态成员函数只能访问静态成员变量
class Person{
public:static int m_A;
};int Person::m_A 100;void test01(){Person p;cout p.m_A endl;Person p2;p.m_A 200;//输出的值为200所以说明所有对象共享同一个数据cout p.m_A endl;
}void test02(){/*Person p;cout p.m_A endl;*/cout Person::m_A endl;
}int main(){test01();system(pause);return 0;
}示例
class Person{
public:static void func(){cout func endl;}static int m_A;int m_B;private:static void func2(){cout func2 endl;}
};int Person::m_A 10;void test01(){Person p1;p1.func();Person::func();//访问不到//Person::func2();
}int main(){test01();system(pause);return 0;
}C对象模型和this指针
成员变量和成员函数分开存储 在C中类内的成员变量和成员函数分开存储 只有非静态成员变量才属于类的对象上
示例
class Person{
public:int m_A;static int m_B;void func(){}static void func2(){}
};int Person::m_B 0;void test01(){Person p;cout sizeof(p) endl;
}void test02(){Person p;cout sizeof(p) endl;
}int main(){system(pause);return 0;
}总结 空对象占用内存空间为1 C编译器会给每个空对象也分配一个空间时为了区分空对象占内存的情况 每个空对象也应该有一个独一无二的内存地址
this指针概念 C通过提供特殊的对象指针this指针解决区分某个对象调用自己this指向被调用的成员函数所属的对象 this指针是隐含每一个非静态成员函数内的一种指针 this指针不需要定义直接使用即可 this指针的用途 当形参和成员变量同名时可用 this 指针来区分 在类的非静态成员函数中返回对象本身可使用 return *this
示例:
class Person{
public:Person(int age){this - age age;}Person PersonAddAge(Person p){this - age p.age;return *this;}int age;
};void test01(){Person p1(18);cout p1.age endl;
}void test02(){Person p1(10);Person p2(10);p2.PersonAddAge(p1).PersonAddAge(p1);cout p2.age endl;
}int main(){test01();test02();system(pause);return 0;
}空指针访问成员函数 C中空指针也是可以调用成员函数的但是也要注意有没有用到 this 指针 如果用到 this 指针需要加以判断保证代码的健壮性
示例
class Person {
public:void showClassName() {cout Person endl;}void showPersonAge() {if (this NULL) {return;}cout m_Age endl;}int m_Age;
};void test01() {Person* p NULL;p-showClassName();//报错因为传入的指针为空//p - showPersonAge();
}int main() {test01();system(pause);return 0;
}const修饰成员函数 常函数 成员函数后加const后称这个数为常函数 常函数内不可以修改成员属性 成员属性声明时加关键字 muteble 后在常函数中依旧可以修改 常对象 声明对象前加const称该对象为常对象 常对象只能调用常函数
示例
class Person{
public:void showPerson() const{//this - m_A 100;this - m_B 100;}int m_A;mutable int m_B;void func(){}
};void test01(){Person p;p.showPerson();
}void test02(){const Person p;//p.m_A 100;p.m_B 100;//p.func();
}int main(){test01();test02();system(pause);return 0;
}总结 this指针的本质是指针常量指针的指向是不可以修改的 this指针不可以修改指针的指向的 在成员函数后面加const修饰的是this指向让指针指向的值也不可以修改 加关键字mutable的特殊变量即使在常函数中也可以修改这个值 加了muteble的特殊变量在常对象下也可以修改 常对象只能调用常函数不可以调用普通成员函数因为普通成员函数可以修改属性
友元 在程序里有些私有属性想让类外特殊的一些函数或者类进行访问就需要用到友元的技术 友元的目的就是让一个函数或者类访问另一个类中私有成员 友元的关键字为 friend 友元的三种实现 全局函数做友元 类做友元 成员函数做友元
全局函数做友元
class Building {//goodGay可以访问Building中的私有成员friend void goodGay(Building building);public:string m_SittingRoom;Building() {m_SittingRoom 客厅;m_BedRoom 卧室;}private:string m_BedRoom;
};void goodGay(Building building) {cout building.m_SittingRoom endl;cout building.m_BedRoom endl;
}void test01() {Building building;goodGay(building);
}int main() {test01();system(pause);return 0;
}类做友元
class Building;class Building {//GoodGay可以访问本类中的私有成员friend class GoodGay;public:Building();string m_SittingRoom;private:string m_BedRoom;
};class GoodGay {
public:GoodGay();void visit();Building* building;
};void GoodGay::visit() {cout building-m_SittingRoom endl;cout building-m_BedRoom endl;
}Building::Building() {m_SittingRoom 客厅;m_BedRoom 卧室;
}GoodGay::GoodGay() {building new Building;
}void test01() {GoodGay gg;gg.visit();
}int main() {test01();system(pause);return 0;
}成员函数做友元
class Building;class GoodGay {
public:GoodGay();//让visit函数可以访问私有成员void visit();void visit2();
private:Building* building;
};class Building {//告诉编译器GoodGay下的visit成员函数作为本类的友元可以访问私有的成员friend void GoodGay::visit();public:Building();string m_SittingRoom;private:string m_BedRoom;
};Building::Building() {m_SittingRoom 客厅;m_BedRoom 卧室;
}GoodGay::GoodGay() {building new Building;
}void GoodGay::visit() {cout building-m_SittingRoom endl;cout building-m_BedRoom endl;
}void GoodGay::visit2(){cout building - m_SittingRoom endl;//cout building - m_BedRoom endl;
}void test01() {GoodGay gg;gg.visit();
}int main() {test01();system(pause);return 0;
}运算符重载 运算符重载概念对已有的运算符重新进行定义赋予其另一种功能以适应不同的数据类型
加号运算符重载 作用实现两个自定义数据类型相加的运算 注意 对于内置的数据类型的表达式的运算符是不可以改变的 不能滥用运算符重载
#include iostream
using namespace std;class Person {
public://成员函数重载/*Person operator(Person p) {Person temp;temp.m_A this-m_A p.m_A;temp.m_B this-m_B p.m_B;return temp;}*/int m_A;int m_B;
};Person operator(Person p1, Person p2) {Person temp;temp.m_A p1.m_A p2.m_B;temp.m_B p1.m_B p2.m_B;return temp;
}Person operator(Person p1, int a) {Person temp;temp.m_A p1.m_A a;temp.m_B p1.m_B a;return temp;
}void test01() {Person p1;p1.m_A 10;p1.m_B 10;Person p2;p2.m_A 10;p2.m_B 10;Person p3 p1 p2;Person p4 p1 100;cout p3.m_A endl;cout p3.m_B endl;cout p4.m_A endl;cout p4.m_B endl;
}int main() {test01();system(pause);return 0;
}左移运算符重载 作用可以输出自定义数据类型 重载左移运算符配合友元可以实现输出自定义数据类型
#include iostream
using namespace std;class Person {friend ostream operator(ostream cout, Person p);public:Person(int a, int b) {m_A a;m_B b;}private:int m_A;int m_B;
};ostream operator (ostream cout, Person p) {cout p.m_A endl;cout p.m_B endl;return cout;
}void test01() {Person p(10,10);cout p endl;
}int main() {test01();system(pause);return 0;
}递增运算符重载 通过重载递增运算符实现自己的整型数据
#include iostream
using namespace std;class MyInteger {friend ostream operator (ostream cout, MyInteger myint);public:MyInteger() {m_Num 0;}//重载前置运算符//返回引用是为了一直对一个数据进行递增操作MyInteger operator() {m_Num;return *this;}//重载后置运算符//参数中的占位参数int可以作为区分前置和后置递增MyInteger operator(int) {MyInteger temp *this;m_Num;return temp;}private:int m_Num;
};ostream operator (ostream cout, MyInteger myint) {cout myint.m_Num;return cout;
}void test01() {MyInteger myint;cout myint endl;
}void test02() {MyInteger myint;cout myint endl;cout myint endl;
}int main() {test01();test02();system(pause);return 0;
}赋值运算符重载 C编译器至少给一个类添加4个函数 1.默认构造函数无参函数体为空 2.默认析构函数无参函数体为空 3.默认拷贝构造函数对属性进行值拷贝 4.赋值运算符 operator对属性进行值拷贝 如果类中有属性指向堆区做复制操作时也会出现深浅拷贝问题
#include iostream
using namespace std;class Person {
public:Person(int age) {m_Age new int(age);}int* m_Age;~Person() {if (m_Age ! NULL) {delete m_Age;m_Age NULL;}}Person operator(Person p) {if (m_Age ! NULL) {delete m_Age;m_Age NULL;}m_Age new int(*p.m_Age);return *this;}
};void test01() {Person p1(16);Person p2(20);Person p3(30);p3 p2 p1;cout *p1.m_Age endl;cout *p2.m_Age endl;cout *p3.m_Age endl;
}int main() {test01();system(pause);return 0;
}关系运算符重载
作用重载关系运算符可以让两个自定义类型对象进行对比操作
示例
#include iostream
using namespace std;class Person {
public:Person(string name, int age) {this-age age;this-name name;}bool operator(Person p) {if (this-age p.age this-name p.name) {return true;}return false;}string name;int age;
};void test01() {Person p1(Tom, 12);Person p2(Tom, 12);cout (p1 p2) endl;
}int main() {test01();system(pause);return 0;
}函数调用运算符重载
函数调用运算符()也可以重载由于重载后使用的方式非常像函数的调用因此成为仿函数仿函数没有固定方法非常灵活
示例
#includeiostream;
using namespace std;class MyPrint {
public:void operator()(string test) {cout test endl;}
};void test01() {MyPrint myprint;//由于使用起来类似于函数调用因此成为仿函数myprint(helloWorld);
}class MyAdd {
public:int operator()(int num1, int num2) {return num1 num2;}
};void test02() {MyAdd myAdd;int res myAdd(1, 2);cout res endl;
}int main() {test01();test02();system(pause);return 0;
}继承
继承是面向对象三大特征之一
继承的基本语法
继承的优点减少重复代码
语法class 子类 : public 父类 子类也被称为派生类 父类也被称为基类
派生类中的成员包含两大部分
一类是从基类继承过来的一类是自己增加的成员从基类继承过来的表现其共性而新增的成员体现了其个性
示例
#includeiostream;
using namespace std;class BasePage {
public:void header() {cout 这是网页头部 endl;}void footer() {cout 这是网页底部 endl;}void left() {cout 这是网页左侧边框展示 endl;}
};class Java : public BasePage {
public:void content() {cout Java学科视频 endl;}
};class Python : public BasePage {
public:void content() {cout Python学科视频 endl;}
};class Cpp : public BasePage {
public:void content() {cout C学科视频 endl;}
};void test01() {cout Java如下 endl;Java ja;ja.content();ja.footer();ja.header();ja.left();cout ------------------ endl;cout Python如下 endl;Python py;py.content();py.footer();py.header();py.left();cout ------------------ endl;cout C如下 endl;Cpp cpp;cpp.content();cpp.footer();cpp.header();cpp.left();
}int main() {test01();system(pause);return 0;
}继承方式
继承的语法class 子类 : 继承方式 父类
继承方式一共有三种
公共继承保护继承私有继承 示例:
#include iostream
using namespace std;class Base1 {
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son1 : public Base1 {
public:void func() {//父类中的公共权限成员 到子类中依然是公共权限m_A 10;//父类中的保护权限成员 到子类中依然是保护权限m_B 20;//父类中的四有权限成员 子类访问不到//m_C 30;}
};void test01() {Son1 s1;s1.m_A 100;//由于是保护权限所以类外访问不到这个成员//s1.m_B 100;
}class Son2 : protected Base1 {
public:void func() {m_A 100;m_B 100;//私有权限依旧访问不到//m_C 100;}
};void test02() {Son2 son2;//protected继承方式将父类中的public权限的变量转换成了protected权限无法类外访问//son2.m_A;//son2.m_B;
}class Son3 : private Base1 {
public:void func() {m_A 10;m_B 10;//依旧无法访问到私有权限成员变量//m_C 10;}
};void test03() {//private继承方式将父类中的成员都变成了private权限无法在外部访问//m_A 1;//m_B 2;
}继承中的对象模型
问题从父类继承过来的成员哪些属于子类对象
父类中私有成员也被子类继承了只是由编译器给隐藏后访问不到
示例
#include iostream;
using namespace std;class Base {
public:int m_A;
protected:int m_B;
private:int m_C;
};class Son : public Base {
public:int m_D;
};//利用开发人员命令提示工具查看对象模型
//跳转盘符 F:
//跳转文件路径 cd 具体路径
//查看命名
//cl /d1 reportSingleClassLayout类名 文件名void test01() {cout size of Son sizeof(Son) endl;
}int main() {test01();system(pause);return 0;
}继承中构造和析构顺序
子类继承父类后当创建子类对象也会调用父类对象中的构造函数
问题父类和子类的构造函数谁先调用
顺序如下
先构造父类再构造子类析构的顺序与构造的顺序相反
示例
#includeiostream;
using namespace std;class Base {
public:Base() {cout Base构造函数 endl;}~Base() {cout Base析构函数 endl;}
};class Son : public Base {
public:Son() {cout Son构造函数 endl;}~Son() {cout Son析构函数 endl;}
};void test01() {Son son;
}int main() {test01();system(pause);return 0;
}继承同名成员处理方式
问题当子类成员与父类出现同名的成员如何通过子类对象访问到子类或父类中同名的数据
访问子类同名成员 直接访问即可访问父类同名成员 需要加作用域
子类对象可以直接访问到子类中同名成员子类对象加作用域可以访问到父类同名成员当子类和父类拥有同名的成员函数子类会隐藏掉父类中同名成员函数加作用域可以访问到父类中同名函数
示例
#includeiostream
using namespace std;class Base {
public:Base() {m_A 100;}void func() {cout Base调用 endl;}int m_A;
};class Son : public Base {
public:Son() {m_A 200;}void func() {cout Son调用 endl;}int m_A;
};void test01() {Son s;cout s.m_A endl;//如果通过子类对象访问到父类中的同名成员需要加作用域cout s.Base::m_A endl;
}void test02() {Son s;s.func();s.Base::func();
}int main() {test01();test02();return 0;
}继承同名静态成员处理方式
问题继承中同名的静态成员在子类对象上如何进行访问
静态成员和非静态成员出现同名处理方式一致
访问子类同名成员 直接访问即可访问父类同名成员 需要加作用域
同名静态成员处理方式和非静态处理方式一样只不过有两种访问方式通过对象和通过类名
示例
#includeiostream;
using namespace std;class Base {
public:static int m_A;static void func() {cout Base静态 endl;}
};int Base::m_A 100;class Son : public Base {
public:static int m_A;static void func() {cout Son静态 endl;}
};int Son::m_A 200;void test01() {//通过对象访问Son s;cout s.m_A endl;cout s.Base::m_A endl;//通过类名访问cout Son::m_A endl;cout Son::Base::m_A endl;
}void test02() {//通过对象访问Son s;s.func();s.Base::func();//通过类名访问Son::func();Son::Base::func();
}int main() {test01();test02();return 0;
}多继承语法
C允许一个类继承多个类
语法class 子类 : 继承方式 父类1,继承方式 父类2...
多继承可能会引发父类中有同名成员出现需要加作用域区分
C实际开发中不建议用多继承
示例
#includeiostream;
using namespace std;class Base1 {
public:Base1() {m_A 1;}int m_A;
};class Base2 {
public:Base2() {m_B 2;}int m_B;
};class Son : public Base1, public Base2 {
public:Son() {m_C 3;m_D 4;}int m_C;int m_D;
};void test01() {Son s;cout sizeof(s) endl;cout s.Base1::m_A endl;cout s.Base2::m_B endl;
}int main() {test01();return 0;
}菱形继承
菱形继承概念
两个派生类继承同一个基类又有某个类同时继承两个派生类这种继承被称为菱形继承或者钻石继承
案例 分析案例中出现的问题
羊继承了动物的数据驼同样继承了动物的数据当羊驼使用数据时会产生二义性羊驼继承自动物的数据变成了两份其实一份即可
示例
#includeiostream;
using namespace std;class Animal{
public:int m_Age;
};//因为菱形继承导致数据有两份资源浪费
//利用虚继承 加上关键字 virtual 变为虚继承
//Animal类称为 虚基类
class Sheep : virtual public Animal {};class Tuo : virtual public Animal{};class SheepTuo : public Sheep,public Tuo{};void test01() {SheepTuo st;st.Sheep::m_Age 10;st.Tuo::m_Age 11;//当菱形继承两个父类拥有相同数据需要加以作用域区分cout st.Sheep::m_Age endl;cout st.Tuo::m_Age endl;cout st.m_Age endl;}int main() {test01();return 0;
}多态
多态的基本概念 多态是C面向对象三大特性之一 多态分为两类 静态多态函数重载和运算符重载属于静态多态复用函数名 动态多态派生类和虚函数实现运行时多态 静态多态和动态多态区别 静态多态的函数地址早绑定 - 编译阶段确定函数地址 动态多态的函数地址晚绑定 - 运行阶段确定函数地址 动态多态满足条件 有继承关系 子类重写父类的虚函数 动态多态使用 父类的指针或引用指向子类对象 重写函数返回值类型 函数名 参数列表完全一致成为重写
示例
#includeiostream;
using namespace std;class Animal {
public:virtual void speak() {cout 说话 endl;}
};class Cat : public Animal {
public:void speak() {cout 猫说话 endl;}
};class Dog :public Animal {
public:void speak() {cout 狗说话 endl;}
};//地址早绑定 在编译阶段确定函数地址
//如果想让猫说话 那么这个函数地址就不能提前绑定 需要在运行阶段进行绑定 地址晚绑定
void doSpeak(Animal animal) {animal.speak();
}void test01() {Cat cat;doSpeak(cat);Dog dog;doSpeak(dog);
}int main() {test01();return 0;
}多态案例一计算器类 案例描述 分别利用普通写法和多态技术设计实现两个操作数进行运算的计算机类 多态的优点 代码组织结构清晰 可读性强 利于前期和后期的拓展和维护
示例
#includeiostream;
using namespace std;class Caculator {
public:int getResult(string oper) {if (oper ) {return num1 num2;}else if (oper -) {return num1 - num2;}else if (oper *) {return num1 * num2;}}int num1;int num2;
};void test01() {Caculator c;c.num1 1;c.num2 2;cout c.getResult() endl;cout c.getResult(-) endl;cout c.getResult(*) endl;
}class AbstractCalculator {
public:virtual int getResult() {return 0;}int num1;int num2;
};class AddCalculator : public AbstractCalculator {
public:int getResult() {return num1 num2;}
};class SubCalculator : public AbstractCalculator {
public:int getResult() {return num1 - num2;}
};class MulCalculator : public AbstractCalculator {
public:int getResult() {return num1 * num2;}
};void test02() {AbstractCalculator* abc new AddCalculator;abc-num1 1;abc-num2 2;cout abc-getResult() endl;delete abc;abc new SubCalculator;abc-num1 1;abc-num2 2;cout abc-getResult() endl;delete abc;abc new MulCalculator;abc-num1 1;abc-num2 2;cout abc-getResult() endl;delete abc;
}int main() {test01();test02();return 0;
}纯虚函数和抽象类 在多态中通常父类中虚函数的实现是毫无意义的主要都是调用子类重写的内容 因此可以将虚函数改为纯虚函数 纯虚函数语法virtual 返回值类型 函数名 (参数列表) 0; 当类中有了纯虚函数这个类也称为抽象类 抽象类特点 无法实例化对象 子类必须重写抽象类中的纯虚函数否则也属于抽象类
示例
#includeiostream
using namespace std;class Base {
public://只要有一个纯虚函数这个类成为抽象类virtual void func() 0;
};class Son : public Base{
public:};class Son2 :public Base {
public:virtual void func() {cout 调用 endl;}
};void test01() {//无论堆区还是栈区都无法实例化对象//Base b;//new Base;//抽象类的子类必须重写父类中的纯虚函数否则也属于抽象类无法实例化对象//Son s;Base* b new Son2;b-func();
}int main() {test01();return 0;
}多态案例二-制作饮品 案例描述 制作饮品的大致流程为煮水、冲泡、导入杯中、加入辅料 利用多态技术实现本案例提供抽象制作饮品基类提供子类制作咖啡和茶叶 示例
#includeiostream;
using namespace std;class AbstractDrinking {
public:virtual void Boil() 0;virtual void Brew() 0;virtual void PourInCup() 0;virtual void PutSomething() 0;void makeDrink() {Boil();Brew();PourInCup();PutSomething();}
};class Coffee : public AbstractDrinking {
public:virtual void Boil() {cout 煮水 endl;}virtual void Brew() {cout 冲咖啡 endl;}virtual void PourInCup() {cout 倒咖啡 endl;}virtual void PutSomething() {cout 加糖 endl;}
};class Tea : public AbstractDrinking {
public:virtual void Boil() {cout 煮矿泉水 endl;}virtual void Brew() {cout 冲茶叶 endl;}virtual void PourInCup() {cout 倒茶叶 endl;}virtual void PutSomething() {cout 加枸杞 endl;}
};void doWork(AbstractDrinking* a) {a-makeDrink();delete a;
}void test01() {doWork(new Coffee);doWork(new Tea);
}int main() {test01();return 0;
}虚析构和纯虚析构 多态使用时如果子类中有属性开辟到堆区那么父类指针在释放时无法调用到子类的析构代码 解决方式 将父类中的析构函数改为虚析构或者纯虚析构 虚析构和纯虚析构共性 可以解决父类指针释放子类对象 都需要有具体的函数实现 虚析构和纯虚析构区别 如果是纯虚析构该类属于抽象类无法实例化对象 虚析构语法virtual ~类名(){} 纯虚析构语法virtual ~类名(){} 0; 类名::~类名(){} 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象 如果子类中没有堆区数据可以不写为虚析构或者纯虚析构 拥有纯虚析构函数的类也属于抽象类
示例
#includeiostream;
#includestring;
using namespace std;class Animal {
public:Animal() {cout Animal构造函数 endl;}//利用虚析构可以解决 父类指针释放子类对象时不干净的问题//virtual ~Animal() {// cout Animal析构函数 endl;//}//纯虚析构//需要声明也需要实现//有了纯虚析构之后这个类也属于抽象类无法实例化对象virtual ~Animal() 0;virtual void speak() 0;
};Animal :: ~Animal() {cout Animal纯虚析构 endl;
}class Cat : public Animal {
public:Cat(string name) {cout Cat构造 endl;m_name new string(name);}~Cat() {if (m_name ! NULL) {cout Cat析构 endl;delete m_name;m_name NULL;}}virtual void speak() {cout *m_name 猫叫 endl;}string* m_name;
};void test01() {Animal* a new Cat(Tom);a-speak();//父类指针在析构的时候 不会调用子类中析构函数 导致子类中如果有堆区属性 出现内存泄漏delete a;
}int main() {test01();return 0;
}多态案例三-电脑组装 案例描述 电脑主要组成部件为CPU用于计算显卡用于显示内存条用于存储 将每个零件封装出抽象基类并且提供不同的厂商生产不同的零件例如Inter厂商和Lenovo厂商 创建电脑类提供让电脑工作的函数并且调用每个零件工作的接口 测试时组装三台不同的电脑进行工作
示例
#includeiostream;
using namespace std;class Cpu {
public:virtual void calculate() 0;
};class VideoCard {
public:virtual void display() 0;
};class Memory {
public:virtual void storage() 0;
};class Computer {
public:Computer(Cpu* m_Cpu, VideoCard* m_vc, Memory* m_mem) {cpu m_Cpu;vc m_vc;mem m_mem;}void work() {cpu-calculate();vc-display();mem-storage();}~Computer() {if (cpu ! NULL) {delete cpu;cpu NULL;}if (vc ! NULL) {delete vc;vc NULL;}if (mem ! NULL) {delete mem;mem NULL;}}private:Cpu* cpu;VideoCard* vc;Memory* mem;
};class InterCPU : public Cpu {virtual void calculate() {cout Inter Cpu endl;}
};class InterVideoCard : public VideoCard {virtual void display() {cout Inter VideoCard endl;}
};class InterMemory : public Memory {virtual void storage() {cout Inter Memory endl;}
};class LenovoCPU : public Cpu {virtual void calculate() {cout Lenovo Cpu endl;}
};class LenovoVideoCard : public VideoCard {virtual void display() {cout Lenovo VideoCard endl;}
};class LenovoMemory : public Memory {virtual void storage() {cout Lenovo Memory endl;}
};void test01() {Cpu* inter new InterCPU;VideoCard* card new InterVideoCard;Memory* mem new InterMemory;Computer* c1 new Computer(inter, card, mem);c1-work();delete c1;Computer* c2 new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);c2-work();delete c2;
}int main() {test01();return 0;
}文件操作 程序运行时产生的数据都属于临时数据程序一旦运行结束都会被释放 通过文件可以将数据持久化 C中对文件操作需要包含头文件fstream 文件类型分为两种 文本文件文件以文本的ASCII码形式存储在计算机中 二进制文件文件以文本的二进制形式存储在计算机中用户一般不能直接读懂它们 操作文件的三大类 ofstream写操作 ifstream读操作 fstream读写操作
文本文件
写文件 写文件步骤如下 包含头文件 #include fstream 创建流对象 ofstream ofs; 打开文件 ofs.open(“文件路径”,打开方式); 写数据 ofs “写入的数据”; 关闭文件 ofs.close(); 文件打开方式
打开方式解释ios::in为读文件而打开文件ios::out为写文件而打开文件ios::state初始位置文件尾ios::app追加方式写文件ios::trunc如果文件存在先删除再创建ios::binary二进制方式 注意文件打开方式可以配合使用利用|操作符 例如用二进制方式写文件 ios::binary | ios::out
示例
#include iostream;
#include fstream;
using namespace std;void test01() {ofstream ofs;ofs.open(test.txt, ios::out);ofs 姓名张三 endl;ofs 性别男 endl;ofs 年龄18 endl;ofs.close();
}int main() {test01();return 0;
}读文件 读文件与写文件步骤相似但是读取方式相对较多 读文件步骤如下 包含头文件 #include fstream 创建流对象 ifstream ifs; 打开文件并判断文件是否打开成功 ifs.open(“文件路径”,打开方式); 读数据 四种方式读取 关闭文件 ifs.close();
示例
#includeiostream;
#includefstream;
#includestring;
using namespace std;void test01() {ifstream ifs;ifs.open(test.txt, ios::in);if (!ifs.is_open()) {cout 打开失败 endl;return;}//第一种读取方法/*char buffer[1024] { 0 };while (ifs buffer) {cout buffer endl;}*///第二种读取方法/*char buffer[1024] { 0 };while (ifs.getline(buffer, sizeof(buffer))) {cout buffer endl;}*///第三种读取方法string buffer;while (getline(ifs, buffer)) {cout buffer endl;}//第四种读取方法不常用/*char c;while ((c ifs.get()) ! EOF) {cout c endl;}*/ifs.close();
}int main() {test01();return 0;
}二进制文件 以二进制的方式对文件进行读写操作 打开方式要指定ios::binary
写文件 二进制方式写文件主要利用流对象调用成员函数write 函数原型ostream write(const char * buffer,int len); 参数解释字符指针buffer指向内存中一段存储空间len是读写的字节数
示例
#includeiostream
#includefstream
using namespace std;class Person {
public:char m_Name[64];int m_Age;
};void test01() {ofstream ofs;ofs.open(person.txt, ios::out | ios::binary);Person p { 张三,18 };ofs.write((const char*)p, sizeof(Person));ofs.close();
}int main() {test01();return 0;
}读文件 二进制方式读文件主要利用流对象调用成员函数read 函数原型istream read(char*buffer,int len); 参数解释字符指针buffer指向内存中一段存储空间len是读写的字节数
示例
#includeiostream
#includefstream
using namespace std;class Person {
public:char m_Name[64];int m_Age;
};void test01() {ifstream ifs;ifs.open(person.txt, ios::in | ios::binary);if (!ifs.is_open()) {cout 打开失败 endl;return;}Person p;ifs.read((char*)p, sizeof(Person));cout p.m_Name endl;cout p.m_Age endl;ifs.close();
}int main() {test01();return 0;
) {cout buffer endl;}*///第三种读取方法string buffer;while (getline(ifs, buffer)) {cout buffer endl;}//第四种读取方法不常用/*char c;while ((c ifs.get()) ! EOF) {cout c endl;}*/ifs.close();
}int main() {test01();return 0;
}二进制文件 以二进制的方式对文件进行读写操作 打开方式要指定ios::binary
写文件 二进制方式写文件主要利用流对象调用成员函数write 函数原型ostream write(const char * buffer,int len); 参数解释字符指针buffer指向内存中一段存储空间len是读写的字节数
示例
#includeiostream
#includefstream
using namespace std;class Person {
public:char m_Name[64];int m_Age;
};void test01() {ofstream ofs;ofs.open(person.txt, ios::out | ios::binary);Person p { 张三,18 };ofs.write((const char*)p, sizeof(Person));ofs.close();
}int main() {test01();return 0;
}读文件 二进制方式读文件主要利用流对象调用成员函数read 函数原型istream read(char*buffer,int len); 参数解释字符指针buffer指向内存中一段存储空间len是读写的字节数
示例
#includeiostream
#includefstream
using namespace std;class Person {
public:char m_Name[64];int m_Age;
};void test01() {ifstream ifs;ifs.open(person.txt, ios::in | ios::binary);if (!ifs.is_open()) {cout 打开失败 endl;return;}Person p;ifs.read((char*)p, sizeof(Person));cout p.m_Name endl;cout p.m_Age endl;ifs.close();
}int main() {test01();return 0;
}