c++核心技术(c++核心编程)
序言
本文章主要介绍C++面向对象编程技术做了详细的讲解
1.内存分区模型
代码区:存放函数体的二进制代码 ,由操作系统进行管理的 全局区:存放全局变量和静态变量以及常量 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
内存四区意义:不同区域存放的数据 ,赋予不同的生命周期, 给我们更大的灵活编程
1.1程序运行前
在程序编译后 ,生成了exe可执行程序 ,未执行该程序前分为两个区域
代码区:
存放 CPU 执行的机器指令
代码区是共享的 ,共享的目的是对于频繁被执行的程序 ,只需要在内存中有一份代码即可
代码区是只读的 ,使其只读的原因是防止程序意外地修改了它的指令
全局区:
全局变量和静态变量存放在此.
全局区还包含了常量区, 字符串常量和其他常量也存放在此.
该区域的数据在程序结束后由操作系统释放.
总结: C++中在程序运行前分为全局区和代码区 代码区特点是共享和只读 全局区中存放全局变量 、静态变量 、常量 常量区中存放 const修饰的全局常量 和 字符串常量1.2程序运行后
栈区:
由编译器自动分配释放, 存放函数的参数值,局部变量等
注意事项:不要返回局部变量的地址 ,栈区开辟的数据由编译器自动释放堆区:
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
在C++中主要利用new在堆区开辟内存总结:
堆区数据由程序员管理开辟和释放
堆区数据利用new关键字进行开辟内存
1.3 new操作符
C++中利用new操作符在堆区开辟数据
堆区开辟的数据 ,由程序员手动开辟 ,手动释放 ,释放利用操作符 delete
语法: new 数据类型
利用new创建的数据 ,会返回该数据对应的类型的指针
示例1: 基本语法
int* func() { int* a = new int(10); return a; } int main() { int *p = func(); cout << *p << endl; cout << *p << endl; //利用delete释放堆区数据 delete p; //cout << *p << endl; //报错,释放的空间不可访问 system("pause"); return 0; }示例2:开辟数组
//堆区开辟数组 int main() { int* arr = new int[10]; for (int i = 0; i < 10; i++) { arr[i] = i + 100; } for (int i = 0; i < 10; i++) { cout << arr[i] << endl; } //释放数组 delete 后加 [] delete[] arr; system("pause"); return 0; }2 引用
2.1 引用的基本使用
**作用:**给变量起别名
语法: 数据类型 &别名 = 原名
示例:
int main() { int a = 10; int &b = a; cout << "a = " << a << endl; cout << "b = " << b << endl; b = 100; cout << "a = " << a << endl; cout << "b = " << b << endl; system("pause"); return 0; }2.2 引用注意事项
引用必须初始化 引用在初始化后 ,不可以改变示例:
int main() { int a = 10; int b = 20; //int &c; //错误 ,引用必须初始化 int &c = a; //一旦初始化后,就不可以更改 c = b; //这是赋值操作 ,不是更改引用 cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "c = " << c << endl; system("pause"); return 0; }2.3 引用做函数参数
**作用:**函数传参时 ,可以利用引用的技术让形参修饰实参
**优点:**可以简化指针修改实参
示例:
//1. 值传递 void mySwap01(int a, int b) { int temp = a; a = b; b = temp; } //2. 地址传递 void mySwap02(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } //3. 引用传递 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:" << a << " b:" << b << endl; mySwap02(&a, &b); cout << "a:" << a << " b:" << b << endl; mySwap03(a, b); cout << "a:" << a << " b:" << b << endl; system("pause"); return 0; }总结:通过引用参数产生的效果同按地址传递是一样的 。引用的语法更清楚简单
2.4 引用做函数返回值
作用:引用是可以作为函数的返回值存在的
注意:不要返回局部变量引用用法:函数调用作为左值
示例
//返回局部变量引用 int& test01() { int a = 10; //局部变量 return a; } //返回静态变量引用 int& test02() { static int a = 20; return a; } int main() { //不能返回局部变量的引用 int& ref = test01(); cout << "ref = " << ref << endl; cout << "ref = " << ref << endl; //如果函数做左值 ,那么必须返回引用 int& ref2 = test02(); cout << "ref2 = " << ref2 << endl; cout << "ref2 = " << ref2 << endl; test02() = 1000; cout << "ref2 = " << ref2 << endl; cout << "ref2 = " << ref2 << endl; system("pause"); return 0; }2.5 引用的本质
本质:引用的本质在c++内部实现是一个指针常量.
2.6 常量引用
**作用:**常量引用主要用来修饰形参 ,防止误操作
在函数形参列表中 ,可以加const修饰形参 ,防止形参改变实参
3.函数提高
略
4.类与对象
C++面向对象的三大特性为:封装 、继承 、多态
C++认为万事万物都皆为对象 ,对象上有其属性和行为
例如: 人可以作为对象 ,属性有姓名 、年龄 、身高 、体重… ,行为有走 、跑 、跳 、吃饭 、唱歌…
车也可以作为对象 ,属性有轮胎 、方向盘、车灯…,行为有载人 、放音乐 、放空调…
具有相同性质的对象 ,我们可以抽象称为类 ,人属于人类,车属于车类
4.1封装
4.1.1 封装的意义
封装是C++面向对象三大特性之一
封装的意义:
将属性和行为作为一个整体 ,表现生活中的事物 将属性和行为加以权限控制封装意义一:
在设计类的时候 ,属性和行为写在一起,表现事物
语法: class 类名{ 访问权限: 属性 / 行为 };
封装意义二:
类在设计时 ,可以把属性和行为放在不同的权限下 ,加以控制
访问权限有三种:
public 公共权限 类内可以访问 类外可以访问 protected 保护权限 类内可以访问 类外不可以访问 private 私有权限 类内可以访问 类外不可以访问4.1.2 struct和class区别
在C++中 struct和class唯一的区别就在于 默认的访问权限不同
区别:
struct 默认权限为公共 class 默认权限为私有4.2 对象的初始化和清理
生活中我们买的电子产品都基本会有出厂设置 ,在某一天我们不用时候也会删除一些自己信息数据保证安全 C++中的面向对象来源于生活 ,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置 。4.2.1 构造函数和析构函数
对象的初始化和清理也是两个非常重要的安全问题
一个对象或者变量没有初始状态 ,对其使用后果是未知
同样的使用完一个对象或变量 ,没有及时清理 ,也会造成一定的安全问题
c++利用了构造函数和析构函数解决上述问题 ,这两个函数将会被编译器自动调用 ,完成对象初始化和清理工作 。
对象的初始化和清理工作是编译器强制要我们做的事情 ,因此如果我们不提供构造和析构 ,编译器会提供
编译器提供的构造函数和析构函数是空实现 。
构造函数:主要作用在于创建对象时为对象的成员属性赋值 ,构造函数由编译器自动调用,无须手动调用 。 析构函数:主要作用在于对象销毁前系统自动调用 ,执行一些清理工作 。构造函数语法:类名(){}
构造函数 ,没有返回值也不写void 函数名称与类名相同 构造函数可以有参数,因此可以发生重载 程序在调用对象时候会自动调用构造 ,无须手动调用,而且只会调用一次析构函数语法: ~类名(){}
析构函数 ,没有返回值也不写void 函数名称与类名相同,在名称前加上符号 ~ 析构函数不可以有参数 ,因此不可以发生重载 程序在对象销毁前会自动调用析构 ,无须手动调用,而且只会调用一次示例
class Person { public: //构造函数 Person() { cout << "Person的构造函数调用" << endl; } //析构函数 ~Person() { cout << "Person的析构函数调用" << endl; } }; void test01() { Person p; } int main() { test01(); system("pause"); return 0; }4.2.2 构造函数的分类及调用
两种分类方式:
按参数分为: 有参构造和无参构造
按类型分为: 普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
示例
//1、构造函数分类 // 按照参数分类分为 有参和无参构造 无参又称为默认构造函数 // 按照类型分类分为 普通构造和拷贝构造 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; }; //2 、构造函数的调用 //调用无参构造函数 void test01() { Person p; //调用无参构造函数 } //调用有参的构造函数 void test02() { //2.1 括号法 ,常用 Person p1(10); //注意1:调用无参构造函数不能加括号 ,如果加了编译器认为这是一个函数声明 //Person p2(); //2.2 显式法 Person p2 = Person(10); Person p3 = Person(p2); //Person(10)单独写就是匿名对象 当前行结束之后 ,马上析构 //2.3 隐式转换法 Person p4 = 10; // Person p4 = Person(10); Person p5 = p4; // Person p5 = Person(p4); //注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明 //Person p5(p4); } int main() { test01(); //test02(); system("pause"); return 0; }4.2.3 拷贝构造函数调用时机
C++中拷贝构造函数调用时机通常有三种情况
使用一个已经创建完毕的对象来初始化一个新对象 值传递的方式给函数参数传值 以值方式返回局部对象4.2.4 构造函数调用规则
默认情况下 ,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参 ,函数体为空)
2.默认析构函数(无参 ,函数体为空)
3.默认拷贝构造函数 ,对属性进行值拷贝
构造函数调用规则如下:
如果用户定义有参构造函数 ,c++不在提供默认无参构造,但是会提供默认拷贝构造
如果用户定义拷贝构造函数 ,c++不会再提供其他构造函数
4.2.5 深拷贝与浅拷贝
深浅拷贝是面试经典问题 ,也是常见的一个坑
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
总结:如果属性有在堆区开辟的 ,一定要自己提供拷贝构造函数 ,防止浅拷贝带来的问题
4.2.6 初始化列表
作用:
C++提供了初始化列表语法 ,用来初始化属性
语法:构造函数():属性1(值1),属性2(值2)... {}
4.4 友元
生活中你的家有客厅(Public) ,有你的卧室(Private)
客厅所有来的客人都可以进去 ,但是你的卧室是私有的 ,也就是说只有你能进去
但是呢 ,你也可以允许你的好闺蜜好基友进去 。
在程序里 ,有些私有属性 也想让类外特殊的一些函数或者类进行访问 ,就需要用到友元的技术
友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的关键字为 friend
友元的三种实现
全局函数做友元 类做友元 成员函数做友元4.4.1 全局函数做友元
class Building { //告诉编译器 goodGay全局函数 是 Building类的好朋友 ,可以访问类中的私有内容 friend void goodGay(Building * building); public: Building() { this->m_SittingRoom = "客厅"; this->m_BedRoom = "卧室"; } public: string m_SittingRoom; //客厅 private: string m_BedRoom; //卧室 }; void goodGay(Building * building) { cout << "好基友正在访问: " << building->m_SittingRoom << endl; cout << "好基友正在访问: " << building->m_BedRoom << endl; } void test01() { Building b; goodGay(&b); } int main(){ test01(); system("pause"); return 0; }4.5 运算符重载
运算符重载概念:对已有的运算符重新进行定义 ,赋予其另一种功能 ,以适应不同的数据类型
示例:
class Person { public: Person() {}; Person(int a, int b) { this->m_A = a; this->m_B = b; } //成员函数实现 + 号运算符重载 Person operator+(const Person& p) { Person temp; temp.m_A = this->m_A + p.m_A; temp.m_B = this->m_B + p.m_B; return temp; } public: int m_A; int m_B; }; //全局函数实现 + 号运算符重载 //Person operator+(const Person& p1, const Person& p2) { // Person temp(0, 0); // temp.m_A = p1.m_A + p2.m_A; // temp.m_B = p1.m_B + p2.m_B; // return temp; //} //运算符重载 可以发生函数重载 Person operator+(const Person& p2, int val) { Person temp; temp.m_A = p2.m_A + val; temp.m_B = p2.m_B + val; return temp; } void test() { Person p1(10, 10); Person p2(20, 20); //成员函数方式 Person p3 = p2 + p1; //相当于 p2.operaor+(p1) cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl; Person p4 = p3 + 10; //相当于 operator+(p3,10) cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl; } int main() { test(); system("pause"); return 0; }4.6继承
4.6.1 继承的基本语法
例如我们看到很多网站中,都有公共的头部 ,公共的底部 ,甚至公共的左侧列表,只有中心内容不同
接下来我们分别利用普通写法和继承的写法来实现网页中的内容 ,看一下继承存在的意义以及好处
普通实现: //Java页面 class Java { public: void header() { cout << "首页 、公开课 、登录 、注册...(公共头部)" << endl; } void footer() { cout << "帮助中心 、交流合作 、站内地图...(公共底部)" << endl; } void left() { cout << "Java,Python,C++...(公共分类列表)" << endl; } void content() { cout << "JAVA学科视频" << endl; } }; //Python页面 class Python { public: void header() { cout << "首页 、公开课 、登录 、注册...(公共头部)" << endl; } void footer() { cout << "帮助中心 、交流合作 、站内地图...(公共底部)" << endl; } void left() { cout << "Java,Python,C++...(公共分类列表)" << endl; } void content() { cout << "Python学科视频" << endl; } }; //C++页面 class CPP { public: void header() { cout << "首页 、公开课、登录 、注册...(公共头部)" << endl; } void footer() { cout << "帮助中心 、交流合作、站内地图...(公共底部)" << endl; } void left() { cout << "Java,Python,C++...(公共分类列表)" << endl; } void content() { cout << "C++学科视频" << endl; } }; void test01() { //Java页面 cout << "Java下载视频页面如下: " << endl; Java ja; ja.header(); ja.footer(); ja.left(); ja.content(); cout << "--------------------" << endl; //Python页面 cout << "Python下载视频页面如下: " << endl; Python py; py.header(); py.footer(); py.left(); py.content(); cout << "--------------------" << endl; //C++页面 cout << "C++下载视频页面如下: " << endl; CPP cp; cp.header(); cp.footer(); cp.left(); cp.content(); } int main() { test01(); system("pause"); return 0; }继承实现:
//公共页面 class BasePage { public: void header() { cout << "首页 、公开课 、登录 、注册...(公共头部)" << endl; } void footer() { cout << "帮助中心 、交流合作 、站内地图...(公共底部)" << endl; } void left() { cout << "Java,Python,C++...(公共分类列表)" << endl; } }; //Java页面 class Java : public BasePage { public: void content() { cout << "JAVA学科视频" << endl; } }; //Python页面 class Python : public BasePage { public: void content() { cout << "Python学科视频" << endl; } }; //C++页面 class CPP : public BasePage { public: void content() { cout << "C++学科视频" << endl; } }; void test01() { //Java页面 cout << "Java下载视频页面如下: " << endl; Java ja; ja.header(); ja.footer(); ja.left(); ja.content(); cout << "--------------------" << endl; //Python页面 cout << "Python下载视频页面如下: " << endl; Python py; py.header(); py.footer(); py.left(); py.content(); cout << "--------------------" << endl; //C++页面 cout << "C++下载视频页面如下: " << endl; CPP cp; cp.header(); cp.footer(); cp.left(); cp.content(); } int main() { test01(); system("pause"); return 0; }总结:
继承的好处:可以减少重复的代码
class A : public B;
A 类称为子类 或 派生类
B 类称为父类 或 基类
派生类中的成员 ,包含两大部分:
一类是从基类继承过来的 ,一类是自己增加的成员 。
从基类继承过过来的表现其共性 ,而新增的成员体现了其个性 。
4.6.2 继承方式
继承的语法:class 子类 : 继承方式 父类
继承方式一共有三种:
公共继承 保护继承 私有继承4.7 多态
4.7.1 多态的基本概念
多态是C++面向对象三大特性之一
多态分为两类
静态多态: 函数重载 和 运算符重载属于静态多态 ,复用函数名 动态多态: 派生类和虚函数实现运行时多态静态多态和动态多态区别:
静态多态的函数地址早绑定 - 编译阶段确定函数地址 动态多态的函数地址晚绑定 - 运行阶段确定函数地址下面通过案例进行讲解多态
class Animal { public: //Speak函数就是虚函数 //函数前面加上virtual关键字 ,变成虚函数 ,那么编译器在编译的时候就不能确定函数调用了 。 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(); } // //多态满足条件: //1 、有继承关系 //2 、子类重写父类中的虚函数 //多态使用: //父类指针或引用指向子类对象 void test01() { Cat cat; DoSpeak(cat); Dog dog; DoSpeak(dog); } int main() { test01(); system("pause"); return 0; }总结:
多态满足条件
有继承关系 子类重写父类中的虚函数多态使用条件
父类指针或引用指向子类对象重写:函数返回值类型 函数名 参数列表 完全一致称为重写
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!