C++ 总结
来源:互联网 发布:官网淘宝店铺装修模板 编辑:程序博客网 时间:2024/05/07 22:16
整理自传智扫地僧教学视频
OOP: 封装 继承 多态
C++对C语言的增强
- register关键字。请求将变量储存在寄存器中。C语言中register关键字修饰的变量不能取地址。C++中支持register关键字,当需要取register修饰的变量的地址时,register对变量的声明变得无效。另外不使用该关键字也可以对代码进行优化。
- C语言中同名的全局变量最终会被链接到全局数据区的同一个地址。C++不允许重定义全局变量。
- C语言中struct定义了一组变量的集合,编译器不认为是一种新的数据类型。C++中的struct是一种新类型的声明。与class关键字的区别
- 三目运算符。 C语言中返回的是一个值,不能做左值;C++中返回的是变量自身,可以作为左值。C语言中可以返回地址,通过间接赋值来实现
C/C++ 中的 const
- C中的const,只读变量,有自己的存储空间,但可通过指针间接修改!
- C++中的const,不一定分配存储空间。当遇到const声明时,以键值对的形式将变量和值储存在符号表中。
- 当对变量取地址时,会为变量开辟存储空间,与符号表中的变量无关。
- 当const常量为全局,并且需要在其它文件中使用时,也会分配存储空间。
#include<iostream>using namespace std;int main(){ const int a = 10; int *p = (int *)&a; *p = 20; cout << a << endl; cout << *p << endl; return 0;}
- 与#define的区别
- const:编译器处理。提供类型检查和作用域检查。
- #define:预处理器处理,单纯的文本替换。(#undef 卸载宏定义)
- const修饰成员函数
- 修饰的是this指针所指的对象, this指针本身是const
int fun() const // int fun(const ClassName *const pThis, ...){ ...}
引用
- 变量名的本质是一段连续存储空间的别名,引用即为一段连续的存储空间取其他别名。普通引用必须要初始化。
- 引用的本质: 常量指针。 type &name <–> type *const name
- 函数返回值是引用(static或全局变量,不要返回局部变量的引用)
- 用变量接,相当于按值传递
- 函数当左值
- 指针的引用 type * & name
常引用 让变量拥有只读属性
- 用变量初始化
int x = 1;const int &y = x;// y只读,不能通过y修改x
- 用字面量初始化
int &x = 1; // 错 字面量没有内存地址const int &x = 1; //可以(const和&)分配一个存储空间,存放字面量的一个副本,引用作为这段空间的别名。
- 成员函数返回引用(返回对象自身)
return *this;
C++对C的函数扩展
- 内联函数 关键字:inline; 替代宏代码片段(#define…)
- inline必须和函数实现写在一起
- 内联函数没有普通函数调用时的额外开销,如:入栈、出栈、返回
- 内联函数是对编译器的一种请求。内联函数由编译器处理,宏代码替换由预处理器处理。
- 默认参数
- 默认参数必须全在形参列表的右边
- 仅出现在函数声明中
- 占位参数
- 函数重载
- 函数的返回值不是判断函数重载的标准。
- 函数重载遇到默认参数 二义性,编译不能通过
类的封装 & 访问控制
- 类是一种数据类型(固定大小内存块的别名),定义类时不分配内存。通过类定义变量时才分配内存。
- 关键字
- private 修饰的成员变量和函数,只能在该类的内部访问(是类的默认属性)
- protected 修饰的成员变量和函数,只能在该类及其子类的内部访问, 用在继承中,将访问的范围扩展至子类的内部
- public 修饰的成员变量和函数,可以在类的内部和外部访问
- 与struct的区别
- struct 默认属性是public
C++中类的实现
- 成员变量和成员函数分开存储
- 非静态成员变量: 存储在对象中 同struct
- 静态成员变量: 存储在全局区中
- 成员函数
- 存储在代码段中
成员函数如何识别具体对象? this指针
非静态成员函数 隐式包含一个指向当前对象的this指针
eg: int fun(int val) –> int fun(ClassName *const pThis, int val)
静态成员函数 不包含指向具体对象的this指针
eg: static int fun() –> int fun()
- 成员变量和成员函数分开存储
友元函数 & 友元类
- 友元函数是全局函数,不是类的成员函数。可以修改类的私有成员。破坏了类的封装。
- B是A的友元类,则B的所有成员函数都是A的友元函数,可以修改A类的私有成员。
静态成员 & 静态成员函数 static
- 静态成员提供了一种同类对象的共享机制。静态成员局部于类,不是对象成员。对象所占的内存空间不包括静态成员。
- 静态成员的初始化: 在外部进行初始化。静态成员不属于类的任何一个对象,不是在创建对象时被定义,因此不是由类的构造函数初始化的。(例外:const整数类型的初始值,可以在类内初始化。P270)
- 静态成员函数
- 调用方法
- ObjectName.FunctionName();
- ClassName::FunctionName();
- 静态成员函数中不能使用非静态成员,不能调用非静态成员函数。 非静态成员属于具体的一个对象。
- 调用方法
构造函数 & 析构函数
- 构造函数 (仅用于初始化对象,在’=’号右边出现的构造函数是初始化临时变量然后进行赋值。)
- 语法相关
- 函数名与类名相同
- 可以含参数,无任何返回类型的声明
- 自动调用或显示调用。
- 构造函数私有,单例
- 构造函数的分类
- 无参构造函数 (只有当未定义任何一个构造函数时,编译器才提供默认的无参构造函数)
- 应用场景: ClassName ObjectName;
- 有参构造函数
- 普通构造函数
- 拷贝构造函数 (若未定义,编译器提供默认的拷贝构造函数, 浅拷贝。)
- 第一个参数是自身类类型的引用(通常为const引用,临时对象不能赋给非const引用),且任何额外参数都有默认值。
- 实参初始化形参(按值传递),会调用拷贝构造函数创建临时对象。
- 函数返回对象时,也会调用拷贝构造函数创建临时对象,原返回对象被析构掉。
- 无参构造函数 (只有当未定义任何一个构造函数时,编译器才提供默认的无参构造函数)
- 临时对象的去留问题(在构造函数中调用其它构造函数是个危险的行为)
- 临时对象不使用,自动被析构。
- 用临时对象初始化对象时,临时对象直接转成要初始化的对象,不会被析构,在这个过程中不调用拷贝构造函数。(赋值不调用拷贝构造函数,只有初始化对象才调用构造函数,只有通过一个对象初始化另一个对象时才调用拷贝构造函数)
- 通过构造函数进行赋值而非初始化时,临时对象会被析构。
- 构造函数的调用方法
- 无参:
- 隐式调用: ClassName ObjectName; 不能这样用: ClassName ObjectName();函数声明
- 显式调用: ClassName ObjectName = ClassName();
- 有参
- 隐式调用: ClassName ObjectName(参数列表);
- 显示调用: ClassName ObjectName = ClassName(参数列表)
- 拷贝构造函数还可以: ClassName ObjectName = ObjectName2; ObjectName2为已初始化的对象。
- 无参:
- 构造函数的初始化列表
- 必须使用初始化列表的场景
- 场景1: 类成员本身是一个类或结构,但它没有无参构造函数。
- 场景2: 类中包含const成员,const成员必须通过初始化列表的方式进行初始化。
- 必须使用初始化列表的场景
- 语法相关
浅拷贝
- 编译器提供的默认拷贝构造函数
- 编译器提供的默认’=’操作
- 使用memcpy()函数,当拷贝的数据中包含指针时,隐含着浅拷贝!!!
- 场景:定义一个容器,当其中存放自定义的类对象时,若对象中包含着指针,存在动态内存分配。此时在拷贝构造函数或重载’=’操作符函数中使用memcpy()函数,造成浅拷贝。最好通过for循环,显式的通过自定义类中重载的’=’操作符进行拷贝赋值。参见自定义的myVector类。
- 场景:定义一个容器,当其中存放自定义的类对象时,若对象中包含着指针,存在动态内存分配。此时在拷贝构造函数或重载’=’操作符函数中使用memcpy()函数,造成浅拷贝。最好通过for循环,显式的通过自定义类中重载的’=’操作符进行拷贝赋值。参见自定义的myVector类。
析构函数 释放对象
- 语法相关
- ~ClassName();
- 不含参数,也没有任何返回类型的声明
- 对象销毁时自动被调用
- 语法相关
内存的动态分配 & 释放
- malloc()、free() 函数
new、delete 操作符 (C++建议)
基础类型/类对象
type *p = new type(表达式); 表达式结果为初始化值, (表达式)可缺省
delete p;
数组
type *p = new type[表达式];
delete [] p;new/delete与malloc/free的区别: 分配类对象时,new可以自动调用构造函数,delete可以自动调用析构函数
- 自定义类型的数据结构 (struct, class),在使用new时, new 后跟构造函数
运算符重载 operator
- 实现方法: 写出函数实现,将函数名替换为 operator* (*代表要重载的运算符)
- 友元函数重载
- 二元操作符: 左操作数为第一个形参,右操作数为第二个形参。
- 一元操作符: 操作数由形参提供。
- 成员函数重载
- 二元操作符: 左操作数是调用对象(通过this指针传递),右操作数由形参提供。
- 一元操作符: 操作数由对象通过this指针传递。
- 友元函数重载
- 一元运算符区分前置和后置
- 后置,形参列表中加一个int占位参数
- 运算符重载的本质是函数调用。
- 重载方法的选择
- 优先选择通过成员函数重载。(不要滥用友元函数)
- 友元函数重载通常用于左右操作数类型不一样的场景。
- 不能通过友元函数重载的运算符: = () [] ->
- 尽量不要重载 && 和 ||
- &&、||内置短路规则, 而操作符重载是通过函数重载实现的, 不能实现短路规则。
- 不能重载的操作符: ‘.’, ‘::’, ‘.*’, ‘?:’, ‘sizeof’
继承
- 类和类之间的关系
- has-a: 包含关系
- use-a: 一个类部分的使用另一个类
- is-a: 继承
- 基类: 父类; 派生类: 子类
- 单继承、多继承
- 派生类的定义
class 派生类名: 基类名表
{
};
基类名表的构成:
访问控制 基类名1, 访问控制 基类名2, … - 继承的特性
- 子类拥有父类的成员变量和成员函数(除了构造和析构),还拥有父类没有的方法和属性。
- 子类是一种特殊的父类, 子类对象可以当父类对象使用。
- 派生类的访问控制
- 影响因素
- 父类的访问控制
- 子类的继承方式
- 语句写在子类的内部还是外部
- 在子类的内部能否访问成员取决于父类中成员的访问控制属性,与继承方式无关。
- 在子类的外部(通过子类对象)能否访问成员取决于父类的访问控制和子类的继承方式
- public继承, 不改变成员的访问控制属性。
- projected继承,父类中的public和projected在子类中是projected.父类的private在子类中仍是private.
- private继承, 子类中继承的成员全为private。
- 影响因素
- 类型兼容性原则
- 对象
- 子类对象可以当作父类对象使用
- 子类对象可以直接赋值给父类对象。
- 子类对象可以直接初始化父类对象。
- 指针 & 引用
- 父类指针可以指向子类对象。
- 父类引用可以直接引用子类对象。
- 对象
- 继承中的对象模型
- 构造
- 创建子类对象时, 先调用父类构造函数对继承的成员进行初始化,再调用子类构造函数。
- 当父类的构造函数有参数时,需要在子类的初始化列表中显式调用。
- 创建子类对象时, 先调用父类构造函数对继承的成员进行初始化,再调用子类构造函数。
- 析构
- 析构子类对象时, 先调用子类析构函数,再调用父类析构函数对继承的成员进行清理。
- 构造
- 继承和组合混合 (子类中包含其他类对象)
- 构造
- 先构造父类,再构造成员变量(子类中的其他类对象),最后构造自己
- 析构
- 先析构自己, 再析构成员变量(子类中的其他类对象), 最后析构父类
- 构造
- 继承中同名变量、同名成员函数的处理方法
- 基类成员的作用域延伸到所有派生类。
- 派生类的重名成员屏蔽基类的同名成员。(若要使用基类同名成员,通过作用于解析操作符::处理。)
- 派生类中的static关键字
- 基类定义的静态成员,将被所有派生类共享。
- 访问控制遵循一般规则。
- 多继承 (摒弃)
- 一个类继承自多个基类。
- 虚继承 (关键字:virtual)
- 场景: 一个类继承自多个基类,而多个基类继承自同一个类A。派生类在访问A中的成员时,存在二义性。
多态
- 根据对象的类型来决定同名函数的调用
- 父类中函数声明virtual,子类中同名函数无论写不写virtual,均默认为virtual。
- virtual关键字的作用
- 无virtual: 静态联编
- 写virtual: 动态联编(迟绑定),在运行的时候,根据具体的对象类型,执行不同的函数。
- virtual关键字的作用
- 实现多态的三个条件
- 继承
- 虚函数重写
- 父类指针或引用
- 虚析构函数
- 场景: 通过父类指针,调用子类对象的析构函数,释放子类资源。
- 重载、 重写、重定义
- 重载
- 同一个类中的函数
- 子类无法重载父类的函数,父类中的同名函数被覆盖
- 重写
- 发生在父类和子类之间
- 加virtual关键字,多态。
- 不加virtual,重定义
- 重载
- 多态原理 vptr指针、虚函数表
- 含有virtual函数的类中自动生成和维护一张虚函数表,存储虚函数的入口地址。由该类创建的对象拥有一个vptr指针,在调用虚函数时,根据对象的vptr指针(编译器不需要区分是子类对象还是父类对象),在所指的虚函数表中查找函数并调用(查找和调用在运行时完成,动态联编。非虚函数,编译器直接确定被调用的函数,静态联编)。
- vptr指针的分步初始化
- 问题: 构造函数中调用虚函数,能发生多态吗?
- 不能。创建子类对象时,调用父类构造函数,vptr指针指向父类的虚函数表,会调用父类的虚函数,当父类构造函数执行完毕后,vptr指针指向子类的虚函数表。
- 可否将类中成员函数都声明为虚函数?
- 构造函数不能被声明为虚函数。其它函数可以,但效率低。虚函数在运行时通过寻址确定要调用的函数。
- 纯虚函数 & 抽象类
- 形式 virtual 类型 函数名(参数列表) = 0;
- 具有纯虚函数的基类称为抽象类。
- 抽象类不能创建对象,但可以声明抽象类的指针。
- 抽象类不能作为返回类型,不能作为参数类型,但可以声明抽象类的引用。
- 面向抽象类编程: 面向一套预定义好的接口编程。
- 抽象类中多继承的应用
面向接口编程和C多态
- 函数指针基本语法
int add(int a, int b){ return a + b;}typedef int (Add) (int, int); //定义函数类型typedef int (*pAdd) (int, int); //定义函数指针类型int (*p) (int, int); //定义函数指针,是个变量
- 函数指针做函数参数
- 实现了任务的实现者和调用者的分离,解耦和。
- 正向调用
- 反向调用
避免头文件重复包含
- 方法1
#ifndef _XXX_H_#define _XXX_H_...#endif
- 方法2
#pragma once
阅读全文
1 0
- C总结
- C 总结
- 【C++】总结
- 【C#】总结
- C---------------------总结
- c总结
- C#--总结
- c 总结
- c总结
- C总结
- C 总结
- 【总结】C语言总结!!!
- 数据类型总结 (C++,C#)
- [C语言]C总结
- 数据类型总结(C++,C#)
- 【C/C++】排序总结
- C/C++/VC随机数总结
- 【C/C++】realloc使用总结
- Windows下给python安装scrapy详细教程
- 在使用Struts时遇到的各种错误总结
- 个人项目经历---玩转山大
- 各种博弈-HDU5754
- [翻译]Unity中的AssetBundle详解(三)
- C++ 总结
- 求通过的最小砖数(阿里笔试)
- HTTP Header 详解
- 全志R16平台的GPIO操作
- tensorflow-BatchNormalization(tf.nn.moments及tf.nn.batch_normalization)
- 新版Dell本本BIOS设置完全手册--U盘装系统bios设置教程
- 2017年迄今最新人工智能资源
- 关于AsyncHttpClient的cz.msebera.android.httpclient.Header
- PHP PDO操作MYSQL