virtual
- 实现类的多态性;基类定义虚函数,子类可以重写该函数;如果使用了virtual关键字,程序将根据引用或指针指向的 对象类型 来选择方法,否则使用引用类型或指针类型来选择方法
- 实现虚继承,避免菱形继承中,重复字段导致的资源浪费
- virtual 函数在地址绑定时是进行晚绑定的,而普通函数是进行早绑定。
C/C++编译:
gcc name.cpp -lstdc++ -o object_name
g++ name.cpp -o object_name#GUN编译器,在windows环境下,生成exe可执行程序。在unix环境下,生成无后缀的可执行文件
-
通常,在一个C++程序中,只包含头文件和源文件,C++支持对不同源文件进行分别编译,最后对编译好的目标文件做一次链接。头文件中只应该包含声明,类定义,const对象和inline函数【头文件包含在多个源文件中,同时,定义只能出现一次,而申明可以重复多次】
-
每个C++程序必须有且仅有一个main函数,返回值必须是int型
-
编译过程中,会检查程序的语法错误、类型错误等。
-
避免头文件的重复包含
#ifndef SALE #define SALE //definition #endif
指针
野指针:指向非法内存空间的指(没有对应读取权限)
const int * p//常量指针,本质是指针,指针指向的值不可以改,但是指针指向可以修改
int * const p//指针常量,本质是常量,指向不可以修改,指向的值可以修改
const int * const p//指针的指向和指针指向的值都不可以修改
vector
vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1)
;而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)
vector支持动态扩展,且迭代器支持随机访问。
vector<int> vec;
vec.empty()//判断容器是否为空
vec.capacity()//返回容器容量
vec.size()//返回元素个数,容量永远大于元素个数
vec.resize()//重新指定元素个数,超出的被删掉,不够的用零补充
vec.push_back(ele);
vec.pop_back(ele);
vec.insert(iterator,ele);
vec.erase(iterator);
vec.clear();
内存分区
代码区:存放函数体的二进制代码,由操作系统进行管理。存放CPU执行的机器指令,共享且只读,程序运行前存在。
全局区:存放全局变量、常量【main函数外】、静态变量【static】、字符串常量,程序运行前存在。
栈区:由编译器自动分配释放,存放函数的参数值,局部变量、常量【常量由const修饰】等,在函数执行完后自动释放。
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
int * p = new int(10)
int * arr = new int[10]//在堆区创建一个数组
delete[] arr//释放数组的时候需要加上中括号
引用与指针
引用必须要初始化,引用一旦初始化后,就不能再修改了,引用本质上是指针常量,内部通过代码转换实现。
int& ref = ele;//int* const ref = &ele;
ref = 200;//*ref = 200
在函数中,千万不要返回局部变量、局部变量的引用或者指向局部变量的指针。
常量引用:只能用于引用常量,需要用const int& ref = 10;
的形式,引用一块合理的内存空间。
常量const
在全局作用域里定义非const变量,意味着在整个程序中都能访问到。在全局作用域声明的const变量是文件的局部变量,不能被其它文件访问,可以通过指定external
,让其在整个程序中能被访问到。【非const变量默认为external】
被const修饰的对象称为常对象,常对象只能修改被mutable
修饰的变量,同时只能调用常函数。
枚举
enum ele {a,b,c}//第一个枚举成员默认赋值为0,后面每个成员比前面的值大1
可以为一个或者多个枚举成员提供初始值
函数的参数和重载
int func(int a, int b = 20, int c = 30)//默认参数
int func(int a, int)//占位参数
1.如果某个位置已经有了默认参数,那么从左往右都必须有默认参数【默认实参需要在形参列表结尾】。
2.函数声明和实现只能有一个默认参数【不可以重定义默认参数】。
同一个作用域下,函数名相同,但是形参列表不同构成函数重载,函数重载可能和默认参数构成函数的二义性,导致编译报错。
class继承
【默认访问权限是private,而结构体的默认访问权限是public】
public:都可以访问
protected:仅在本类和派生类中可以访问
private:仅在本类中或本类的成员函数可以访问
class 子类:public 父类(, public 父类){//公共继承
}
公有继承:无法访问私有内容,保护权限下的属性仍然为保护权限,私有权限下的属性仍然为私有
保护继承:无法访问私有内容,剩下所有属性都变成保护权限
私有继承:无法访问私有内容,剩下所有属性都变成私有权限
多态
子类重写父类的虚函数,在父类引用指向子类时实现函数地址的晚绑定,
1.定义子类时,先调用父类构造函数,再调用子类构造函数,析构时则先调用子类,再调用父类
2.子类中定义的同名内容将覆盖父类中所有同名内容【例如子类定义的同名函数将覆盖掉父类中所有的同名函数,包括父类中重载的部分】,可以通过子类.父类::父类同名内容
的方式访问父类被覆盖的变量
3.当多继承中,多个父类出现了同名的内容,也需要加上作用域访问
获取类的分布图
#利用编译器打印类的分布图
cl /dl reportSingleClassLayout类名称 文件名称
构造函数
创建一个类,编译器会为每个类添加默认构造函数、析构函数和拷贝构造函数。如果写了构造函数,编译器不再提供构造函数,但是依然提供拷贝构造函数。如果写了拷贝构造函数,编译器不再提供任何构造函数。
className(para){}//构造函数
className(const ClassName &P){}//拷贝构造函数
~className(){}
//括号法调用构造函数
ClassName a;//调用默认构造函数,不要加小括号,ClassName a()会被误解为函数的声明
ClassName a(para);
//显示法调用构造函数
ClassName a = ClassName();
ClassName a = ClassName(para);
ClassName(para)//创建匿名对象
构造函数与析构函数没有返回值,也不写void,函数名称与类名相同,析构函数没有参数。
静态成员函数
所有对象共享静态成员函数和静态成员变量,静态成员函数只能访问静态成员变量
ClassName c;
c.func();//通过对象访问静态成员函数
ClassName::func();//通过类名直接访问静态成员函数
对象的存储
- C++编译器会为每个空对象分配一个字节空间,是为了区分空对象占内存的位置。
- 非静态变量存储在类对象中,静态成员变量和成员函数不存储在类对象中【只有一份】
this指针【指针常量】
this指针存在于每一个非静态成员函数中,指向被调用的成员函数所属的对象。
*this返回对象本身
ClassName& func(para){
return *this;//链式编程思想,返回调用者本身,但是一定要返回本身的引用
}
在成员函数后面加const,修饰的是this指针const ClassName * const this
,此时,const指向的值不允许发生修改。
map/multimap
在map中,所有的元素都会根据元素的键值自动排序。在multimap中,允许有相同的key值,而在map中则不允许。
#include<map>
map<T1,T2> mp;//默认构造函数
map(const map &mp);//拷贝构造函数
map.insert(pair<type,type>(para,para));//插入pair数据
map[3]="string"//数组方式插入
map<int ,string > ::iterator it;;
it = mapName.find(112);//查找,然后返回一个对应的迭代器。
iterator erase(iterator it)//删除一个条目
find(key)//若key值存在,则返回该元素的迭代器,若不存在,返回map.end()
count(key) //统计key的元素个数
结构体
自定义的结构体可用于存储不同的数据类型
(struct) StructName s = {...}
struct StructName{
...
}s;//在定义的时候直接创建一个变量
//创建结构体数组
(struct) StructName s[10] = {
{...},
{...},
{...}
}
//结构体指针
memset、memcpy和strcpy的区别
memcpy是内存拷贝函数,可以拷贝任何数据类型的对象,例如memcpy(b, a, sizeof(b))。
strcpy只能拷贝字符串,遇到’\0′结束拷贝。
memset用来对一段内存空间全部设置为某个字符,例如:char a[100];memset(a, '', sizeof(a))。
内联函数
编译时展开,将函数代码直接嵌入到目标代码中去,用于解决一些频繁调用的小函数大量消耗栈空间的问题inline type func() {};
而宏定义在预编译时展开,只是简单的文本替换。
编译的四个阶段
第一阶段:预处理阶段。根据文件中的预处理指令来修改源文件的内容。如#include指令,作用是把头文件的内容添加到.cpp文件中。
第二阶段:编译阶段,将其翻译成等价的中间代码或汇编代码。
第三阶段:汇编阶段,把汇编语言翻译成目标机器指令。
第四阶段:是链接,例如,某个源文件中的函数可能引用了另一个源文件中定义的某个函数;在程序中可能调用了某个库文件中的函数。
函数指针
int *a[10]; //一个有10个指针的数组,该指针是指向一个整型数的
int (*a)[10]; //一个指向有10个整型数数组的指针
int (*a)(int); //一个指向函数的指针,该函数有一个整型参数并返回一个整型数
int (*a[10])(int); // 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
static
static数据成员独立于该类的任意对象而存在;static数据成员(const static数据成员除外)在类定义体内声明,必须在类外进行初始化
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)