文章目录
- 编译内存相关
- 编译
- 变量与内存分区
-
- 内存对齐
- 内存泄露
- 智能指针
- include "" 和<>
- 语言对比
- c++ 11
- 自动类型推导
- lembda 表达式 (匿名函数)
- 范围for
- delete 函数和 default 函数
- 对比C、python
- 面向对象
-
- 关键字和库函数
- 类相关
- 拷贝构造函数
- 虚函数 与 纯虚函数
- 虚函数实现机制
- 单继承多继承的虚函数表结构
- 语言特性
- 左值引用、右值引用、std::move()
- 指针
- nullptr 与 NULL
-
- 指针与引用
- 强制类型转换
- static_cast
- dynamic_cast
- const_cast
- reinterpret_cast
- 判断结构体相对
- 参数值传递、引用传递、指针传递
- 模板
-
- 可变参数模板
-
- 泛型编程如何实现
- 设计模式
参考:
- https://blog.csdn.net/luanfenlian0992/article/details/118771472#t13
- https://zhuanlan.zhihu.com/p/335994370
- https://zhuanlan.zhihu.com/p/79883965
- https://blog.csdn.net/K346K346/article/details/85018985
- https://blog.csdn.net/weixin_45626706/article/details/116497723?ops_request_misc=&request_id=&biz_id=102&utm_term=slam%20%E9%9D%A2%E8%AF%95c++%E9%97%AE%E9%A2%98&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-0-116497723.nonecase&spm=1018.2226.3001.4187#t39
编译内存相关
编译
编译.cpp .h
过程分为四个过程:编译(编译预处理、编译、优化.s
),汇编.o
,链接。
静态链接
动态链接
变量与内存分区
内存分区
-
栈区:
正常函数体内{}"
定义的都是。
连续内存。
-
堆区:
new
出来的。
类似链表的分段内存。
-
全局/静态存区:
static
和全局变量extern
.
-
常量区:
常量。
-
代码区:
编译后的二进制文件。
变量类型
-
全局变量:
放在.cpp
文件里面的,不再{}
内的变量,同样的名称多文件共享。
多个cpp
文件共用或者{}
内使用需要extern
修饰声明。
-
静态全局变量:
static
修饰,具有文件作用域,多个文件同样的名称是不同的变量。
-
局部变量:
{}
里面定义的。
-
静态局部变量:
static
修饰的{}
函数中的变量,具有函数作用域,只初始化一次。
内存对齐
对齐存取快,不报异常好迁移。
有些设备不支持非对齐的存取,不能保证原子操作。
内存泄露
new
出来的内存,由指针进行操作,后来没delete
指针指向别的了,之前的那块内存地址就找不到了,没法删除啥的。
智能指针
智能指针,自动释放内存,防止内存泄露。
-
shared_ptr
多个指针共享一块内存,通过引用计数自动释放。
-
unique_ptr
一个指针独占一块内存,不能拷贝赋值。可以移动构造和移动赋值构造。
-
weak_ptr
指向 shared_ptr 指向的对象,能够解决由shared_ptr带来的循环引用问题。
include “” 和<>
https://www.iteye.com/blog/kooyee-340846解释:
<>
先去系统目录中找头文件,如果没有在到当前目录下找。所以像STL
标准的头文件 stdio.h、stdlib.h
等用这个方法。
而""
首先在当前目录下寻找,如果找不到,再到系统目录中寻找。 这个用于include自定义的头文件,让系统优先使用当前目录中定义的。
语言对比
c++ 11
自动类型推导
auto
auto var = val1 + val2; //定义+初始化为左边的值
decltype
decltype(val1 + val2) var1 = 0; //定义+初始化为其他值
lembda 表达式 (匿名函数)
[capture list/*捕获的局部变量列表*/] (parameter list/*形参*/) -> return type/*返回变量类型*/
{
/*具体函数实现*/
function body;
};
范围for
for (declaration /*列表中的一个元素*/: expression/*列表、数组*/){
statement
}
这玩意不和python的for差不多吗,多了个declaration的变量类型常用auto
。
delete 函数和 default 函数
delete 函数:= delete
表示该函数不能被调用。default 函数:= default
表示编译器生成默认的函数,例如:生成默认的构造函数。
#include <iostream>
using namespace std;
class A
{
public:
A() = default; // 表示使用默认的构造函数
~A() = default; // 表示使用默认的析构函数
A(const A &) = delete; // 表示类的对象禁止拷贝构造
A &operator=(const A &) = delete; // 表示类的对象禁止拷贝赋值
};
int main()
{
A ex1;
A ex2 = ex1; // error: use of deleted function 'A::A(const A&)'
A ex3;
ex3 = ex1; // error: use of deleted function 'A& A::operator=(const A&)'
return 0;
}
对比C、python
- C++:面向对象
- C:面向过程
- python:解释执行
面向对象
特性
- 封装:将对象的数据和过程绑定在一起则被称为封装。
- 继承
- 多态:子类重写父类的
virtual
虚函数实现。
重载、重写、隐藏的区别
-
重载:
就是函数的重载,和面向对象没关系
-
隐藏:
子类的同名函数会隐藏父类的同名函数,如果需要调用父类函数需要.Base::fun()
-
重写(覆盖):虚函数,纯虚函数(子类一定要重写)
父类的函数有virtual
修饰,函数名、参数列表、返回值类型需要一致。
主要应用:父类指针指向new
子类对象,实现父类指针调用子类的函数。
关键字和库函数
这玩意太多了还是直接看别人写的吧
explicit
:避免编译器进行隐式类型转换static
:
- 修饰类内的变量函数时候,可以不创建类对象直接使用,不能定义为
virtual
函数。只能访问static
成员。因为静态成员函数没有 this 指针。 - 静态数据成员的类型可以是所属类的类型,而普通数据成员的类型只能是该类类型的指针或引用。
const
:修饰常量不能更改,只能初始化时候赋值。mutable
修饰使得const
的某些成员一直保持是可以改变的。#define
:在编译预处理阶段进行文本替换typedef
:定义类型的别名inline
:内联函数。delete
、delete[]
new
、 malloc
、delete
、 free
:malloc、free 是库函数,而new、delete 是关键字
new
:内存分配成功,返回该对象类型的指针;分配失败,抛出 bac_alloc 异常。申请空间时,无需指定分配空间的大小,编译器会根据类型自行计算;申请空间时,返回的类型是对象的指针类型,无需强制类型转换,是类型安全的操作符;malloc
:成功申请到内存,返回指向该内存的指针;分配失败,返回 NULL 指针。在申请空间时,需要确定所申请空间的大小。申请空间时,返回的是 void* 类型,需要进行强制类型的转换,转换为对象类型的指针。- 对于自定义的类型,new 首先调用 operator new() 函数申请空间(底层通过 malloc 实现),然后调用构造函数进行初始化,最后返回自定义类型的指针;delete 首先调用析构函数,然后调用 operator delete() 释放空间(底层通过 free 实现)。malloc、free 无法进行自定义类型的对象的构造和析构。
- new 操作符从自由存储区上为对象动态分配内存,而 malloc 函数从堆上动态分配内存。(自由存储区不等于堆)
- malloc 的原理?malloc 的底层实现?
- C 和 C++ struct 的区别?为什么有了 class 还保留 struct?
union
:联合体,只有一个有效的成员,对联合体的不同成员赋值,将会对覆盖其他成员的值,联合体的大小为其内部所有变量的最大值,按照最大类型的倍数进行分配大小。- class 和 struct 的异同
- class(private 继承),struct(public 继承)
- class 可以用于定义模板参数,struct 不能用于定义模板参数。
volatile
:多线程巴拉巴拉的,类似线程锁。extern
:C 语言编写的函数- sizeof(1==1) 在 C 和 C++ 中分别是什么结果?
- memcpy 函数的底层原理?
- strcpy 函数有什么缺陷?
14.-17.啥的从来没用到过QAQ
类相关
拷贝构造函数
- 值传递: 对于内置数据类型的传递,直接赋值拷贝给形参(形参表示为函数内的局部变量)。对于类类型的传递,首先要调用该类的拷贝构造函数来初始化形参
- 引用传递: 无论是内置数据类型的传递还是类类型,传递引用或指针最终都是传递地址值,不会有拷贝构造函数的调用
需要是引用传递,因为值传递首先需要调用拷贝构造函数创建一个类的对象。禁止套娃。
虚函数 与 纯虚函数
- 虚函数必须实现,否则编译器会报错;
- 包含纯虚函数
virtual fun()=0;
的类为抽象类不能实例化对象。 - 子类继承抽象类,没有完全重写纯虚函数,也是抽象类。
虚函数实现机制
类的对象存储指向虚函数表的指针实现
- 虚函数表
- 虚函数表存放的内容:类的虚函数的地址。
- 虚函数表建立的时间:编译阶段,即程序的编译过程中会将虚函数的地址放在虚函数表中。
- 虚表指针保存的位置:虚表指针存放在对象的内存空间中最前面的位置,这是为了保证正确取到虚函数的偏移量。
单继承多继承的虚函数表结构
语言特性
左值引用、右值引用、std::move()
右值引用可以修改右值,借助 std::move()
转换左值引用为右值引用
- 右值引用
int &&a = 10;
a = 100;
int b = 5;
int &&refb=std::move(b);
std::move()
https://zhuanlan.zhihu.com/p/335994370总结的右值引用好处
- 从性能上讲,左右值引用没有区别,传参使用左右值引用都可以避免拷贝。
- 右值引用可以直接指向右值,也可以通过std::move指向左值;而左值引用只能指向左值(const左值引用也能指向右值)。
- 作为函数形参时,右值引用更灵活。虽然const左值引用也可以做到左右值都接受,但它无法修改,有一定局限性。
指针
大小:64位计算机占8个字节
- 普通指针
- 常量指针
- 常量指针,
const int *myptr = a
,a
可以是变量,只是限制不能使用常量指针修改变量a
值。 - 指针常量,
int const * myptr = a
,myptr
本身不能改变即指向的地址不能改变,a
的值可以进行修改。
- 函数指针
- 函数指针,
int (*funptr)(int x, int y);
,funptr
可以指向函数int fun(int x, int y){return x*y;}
使用funptr = fun;
- 函数指针,函数的返回值为指针
int * fun(){int * intptr = new int(10);return intptr;}
- 指向对象成员的指针
- this指针
- 野指针和空悬指针
- 野指针,未初始化,指向的地址随机
- 空悬指针,指向的内存空间已经释放
nullptr 与 NULL
nullptr 具有数据类型,可以转换为任意类型的指针。
NULL 是0,函数重载调用时,会出现无法匹配的情况。
nullptr
nullptr
并非整型类别,甚至也不是指针类型,但是能转换成任意指针类型。nullptr的实际类型是std:nullptr_t。
NULL
总结别人的解释https://zhuanlan.zhihu.com/p/79883965
c语言中
#define NULL ((void*)0)
c++语言中
#define NULL 0
NULL
是宏定义,在C++中,NULL不过是0,把它当成空指针只是一个无可奈何了。因为C++中不能将void *类型的指针隐式转换成其他指针类型。
指针与引用
- 指针所指向的内存空间在程序运行过程中可以改变,而引用所绑定的对象一旦绑定就不能改变。(是否可变)
- 指针本身在内存中占有内存空间,引用相当于变量的别名,在内存中不占内存空间。(是否占内存)
- 指针可以为空,但是引用必须绑定对象。(是否可为空)
- 指针可以有多级
int **myptp
,但是引用只能一级。(是否能为多级)
强制类型转换
static_cast
int a = 10;
doubel b = static_cast<double>(10);
- 可以:整型和浮点型、字符型之间的互相转换。空指针转化成目标类型的空指针。任何类型的表达式转化成 void 类型。
- 不能:在不同类型的指针之间互相转换,整型和指针,不同类型的引用之间。
- 父类和子类,子类转换为父类安全,父类转换为子类有风险。
dynamic_cast
通过“运行时类型检查”来保证安全性的。
- 可以:专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 NULL 指针。
const_cast
去除 const 属性的转换。
- 可以:将 const 引用转换为同类型的非 const 引用。将 const 指针转换为同类型的非 const 指针。
- 不能:用于去掉变量的常量性。
reinterpret_cast
reinterpret_cast 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个bit复制的操作。
就是随便程序员转换,挂了自己负责。
判断结构体相对
自定义比较函数,重载运算法==
啥的。
memcmp
是逐个字节比较,然而可能存在内存对齐,对齐时候的补的字节是随机的。
参数值传递、引用传递、指针传递
模板
参考https://blog.csdn.net/K346K346/article/details/85018985
- 函数模板、类模板区别:
- 实例化方式不同:函数模板实例化由编译程序在处理函数调用时自动完成,类模板实例化需要在程序中显式指定。
- 默认参数
template<typename T1=int,typename T2>
:类模板在模板参数列表中可以有默认参数。c++11
之后类模板和函数都可以有。 - 特化:函数模板只能全特化;而类模板可以全特化,也可以偏特化。
- 调用方式不同:函数模板可以隐式调用,也可以显式调用;类模板只能显式调用。
函数模板
template<typename T>
T fun(T x, T y)
{
return x+t;
}
int x,y;
fun(x,y);//隐式调用
fun<int>(x,y);//显式调用
类模板
template <typename T>
class AAA
{
public:
//构造函数
AAA(T a, T b)
{
this->a = a;
this->b = b;
}
//运算符重载
AAA<T> operator+(AAA &c)
{
AAA<T> tmp(this->a + c.a, this->b + c.b);
cout << tmp.a << " " << tmp.b << endl;
return tmp;
}
private:
T a;
T b;
};
Complex<int> a(10, 20);//显式调用
可变参数模板
template <typename T, typename... Args> // Args 是模板参数包
void fun(T &t, Args&... rest); // 可变参数模板,rest 是函数参数包
template <typename T>
void fun(T &t);
本质上是递归调用fun( T &t, Args&... rest)
,直达参数只剩一个调用fun(T &t)
结束。
#include <iostream>
using namespace std;
template <typename T>
void print_fun(const T &t)
{
cout << t << endl; // 最后一个元素
}
template <typename T, typename... Args>
void print_fun(const T &t, const Args &...args)
{
cout << t << " ";
print_fun(args...);
}
int main()
{
print_fun("Hello", "wolrd", "!");
return 0;
}
/*运行结果:
Hello wolrd !
*/
特化
类模板
template<class T1,class T2>
class Test
{}
//全特化
template<>
class Test<int,int>
{}
//偏特化
template<class T>
class Test<int,T>
{}
函数模板
//普通模板
template<class T1,class T2>
bool Compare(T1 a, T2 b)
{
return a == b;
}
//函数模板特化
template<>
bool Compare(const char* a, const char* b)
{
return strcmp(a,b) == 0;
}
泛型编程如何实现
模板
容器
迭代器
STL中了解一下底层实现
设计模式
ennn再说吧
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)