C++相关

内敛函数

        1.内敛用函数的语法解决的事。宏定义难以实际使用,且不支持调试,也没有类型安全的检查。只有几行的函数进行函数调用创建栈帧等操作耗时,使用内敛提前替换可提高效率。
        2.inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
        3.inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
        4.inline不建议声明和定义分离(不同文件),分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
        关键字inline必须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前面不起任何作用。

lamda表达式

   lambda表达式是C++11中引入的一项新技术,利用lambda表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象,并且使代码更可读。其好处是:使用匿名函数,可以免去函数的声明和定义。这样匿名函数仅在调用函数的时候才会创建函数对象,而调用结束后立即释放,所以匿名函数比非匿名函数更节省空间

lamda函数基本格式   

[捕获列表] (形参列表) mutable 异常列表-> 返回类型
{
    函数体
}
  • 捕获列表:捕获外部变量,捕获的变量可以在函数体中使用(可省略,即不捕获外部变量)
    []:默认不捕获任何变量;    [=]:默认以值捕获所有变量;   [&]:默认以引用捕获所有变量;
    [x]:仅以值捕获x,其它变量不捕获;  [&x]:仅以引用捕获x,其它变量不捕获;
    [=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获;
    [&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;
    [this]:通过引用捕获当前对象(其实是复制指针);
    [*this]:通过传值方式捕获当前对象;
  • 形参列表:和普通函数的形参列表一样(可省略,即无参数列表)
  • mutable:mutable 关键字,如果有,则表示在函数体中可以修改捕获变量
  • 异常列表:noexcept / throw(...),和普通函数的异常列表一样,可省略,即代表可能抛出任何类型的异常。
  • 返回类型:和函数的返回类型一样。可省略,如省略,编译器将自动推导返回类型。
  • 函数体:代码实现。可省略,但是没意义。
void LambdaDemo()
{
    int a = 1;
    int b = 2;
    auto lambda = [a, b](int x, int y)mutable throw() -> bool
    {
        return a + b > x + y;
    };
    bool ret = lambda(3, 4);
}

底层原理

编译器实现 lambda 表达式大致分为一下几个步骤:

  1. 创建 lambda匿名类,实现构造函数,使用 lambda 表达式的函数体重载 operator()
  2. 创建 lambda 对象
  3. 通过对象调用 operator()

然后编译器将lamda表达式翻译后的代码:

class lambda_xxxx
{
private:
    int a;
    int b;
public:
    lambda_xxxx(int _a, int _b) :a(_a), b(_b)
    {
    }
    bool operator()(int x, int y) throw()
    {
        return a + b > x + y;
    }
};
void LambdaDemo()
{
    int a = 1;
    int b = 2;
    lambda_xxxx lambda = lambda_xxxx(a, b);
    bool ret = lambda.operator()(3, 4);

        首先,lambda表达式会被编译器转化为一个匿名的函数对象。这个函数对象会包含lambda表达式中捕获的变量,并且可以被调用。其次,lambda表达式会生成一个闭包,用于保存捕获的变量的值。这个闭包会在lambda表达式被调用时被创建,并在lambda表达式执行完毕后被销毁。最后,lambda表达式的类型是一个匿名的函数对象类型,可以被赋值给相应的函数指针或函数对象。
        在C++14中,还引入了对对象移动到闭包的直接支持。这意味着可以在lambda表达式中使用std:move来移动对象,而不需要手动创建一个闭包。这样可以更方便地在lambda表达式中使用移动语义。
        总之,lambda表达式在底层是通过生成一个匿名的函数对象来实现的,这个函数对象包含了捕获的变量,并且可以被调用。同时,lambda表达式还可以被赋值给相应的函数指针或函数对象。在C++14中,还提供了直接支持对象移动到闭包的功能。

函数没有调用析构

        析构函数作用:在C++中,当一个对象的生命周期结束时,系统会自动调用该对象的析构函数。析构函数的主要作用是释放对象所占用的资源,比如释放动态分配的内存、关闭文件等。

        为什么会没有正确调用析构函数:
        一种常见的情况是程序中出现了异常。程序抛出异常时,会导致程序跳转到异常处理代码,而不会继续执行后续的代码。如果在异常处理代码中没有正确调用析构函数,就会导致对象的资源没有被释放
        另外一种情况是程序中出现了内存泄漏。当我们使用new关键字动态分配内存时,需要手动调用delete来释放内存。如果忘记调用delete,就会导致内存泄漏。而析构函数通常会在对象被销毁时调用delete来释放动态分配的内存。如果没有调用析构函数,就会导致内存泄漏的问题没有调用析构函数可能会导致一系列的问题。

纯虚函数与override

        基类的纯虚函数:在虚函数后面加上“=0”,纯虚函数在基类中没有函数体,只声明,在派生类中定义;               
        virtual void update(const int& sum) = 0;
        子类继承并对纯虚函数进行实现(子类重写基类虚函数),使用保留字“override”。

        子类重写基类中虚函数:override保留字表示当前函数重写了基类的虚函数。
        virtual void update(const int& sum) override;
        目的:1.在函数比较多的情况下可以提示读者某个函数重写了基类虚函数(表示这个虚函数是从基类继承,不是派生类自己定义的);2.强制编译器检查某个函数是否重写基类虚函数,如果没有则报错。

二叉树相关

1.二叉平衡搜索树(AVL):对于二叉搜索树的所有结点,其左右节点的高度之差不超过 1

        给定值的比较次数等于给定值节点在二叉排序树中的层数。如果二叉排序树是平衡的,则n个节点的二叉排序树的高度为Log 2n+1,其查找效率为O(Log 2n),近似于折半查找。如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。一般的,二叉排序树的查找性能在O(Log 2n)到O(n)之间

2.红黑树的时间复杂度为Olog(n)。

大量的ifelse如何优化

1.使用面对对象抽象构造对象
        对这份数据进行分析,看这份数据在哪些地方的处理逻辑是不一样,哪些地方的处理逻辑是一样的。然后抽象出一致的内容。作为父类。 然后再根据不一样的内容。拆分成一个个的子类。 使用多态,就实现了对父类,设定的统一接口进行操作,但是不同的子类对象走不同的逻辑。

左值引用和右值引用

区别:

左值引用的目的是防止函数在进行传参和返回值的时候进行对象拷贝。右值引用是为了移动语义和完美转发。
左值可以放在 = 的左边,右值只能放在 = 的右边。

回调函数

        将函数的指针(地址)作为参数传递给另一个函数,当这个指针用来调用其所指向的函数时,便是回调函数。回调函数不由程序员实现直接调用,而是在特定环境下由其他函数调用,用于对该事件或条件进行响应。

bind和function

        在设计回调函数的时候,无可避免地会接触到可回调对象。C++语言中有几种可调用对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。在C++11中,提供了std::function和std::bind两个方法来对可回调对象进行统一和封装。bind用来将可调用对象与其参数一起绑定,绑定后结构可使用function用来保存并延迟调用。

bind

        bind可以根据当前已有的可调用对象,构造出一个新的可调用对象,有了bind,我们可以实现“动态生成新的函数”的功能。简而言之,就是可以通过bind函数修改原函数并生成一个可以被调用的对象,类似于函数的重载,但是我们又不需要去重新写一个函数,用bind函数就可以实现。
  调用bind的一般形式:auto newCallable = bind(callable,arg_list);

std::function<void(int,int)> fc = std::bind(&A::fun_3, a,std::placeholders::_1,std::placeholders::_2);

       bind作用:在对某个函数进行绑定时,可以指定部分参数或全部参数,也可以不指定任何参数,还可以调整各个参数间的顺序。对于未指定的参数,可以使用占位符_1、_2、_3来表示。_1表示绑定后的函数的第1个参数,_2表示绑定后的函数的第2个参数,其他依次类推。bind可以绑定到普通函数、函数对象、类的成员函数和类的成员变量。

bind缺点:(1)可以创建一个根本没法调用的“可调用对象”而编译器不报错

auto lambda = [](std::unique_ptr<int>) {};
auto b = std::bind(lambda, std::make_unique<int>());

        这段代码可编译,但b一开始就是错的,因为unique_ptr不能拷贝复制,而bind对所有绑定参数都是按值传递。直到试图写b()才会报错。

        lambda相较于`std::bind`而言,具有可读性好、表达力强、可运行效率高

function

        std:function是一种通用,多态的函数封装。可容纳各种可调用对象,例如普通函数,函数指针,Lambda表达式以及std:bind表达式等。换句话说,可以当作是函数的容器。对于类型不安全的函数指针来说,将其封装成一个安全的std::function对象是一个良好的选择。可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。
        function的作用是将具有相同调用形式的不同类型可调用对象进行类型统一。相同的调用形式可以简单理解为:参数列表和返回值相同。

/*  使用map映射字符到可调用对象。
	但需要注意,映射的前提是function对不同类型的可调用对象
进行了类型统一。 */
map<char, function<int(int, int)>> cacul{
    {'+', add},
    {'-', sub},
    {'*', prod()}
};

cout << cacul['*'](5, 4) << endl;

实现原理

通过类的多态,即通过虚表来达到多态;

future

c++11多线程编程的方法

模板函数为什么声明和定义都放在.h文件中

c++四种强制类型转换

main函数使用前后分别执行了什么

虚基类,B继承了A,B中有几个虚函数表?多继承中有几个虚函数表

菱形继承有什么问题

那些方式可以在编译前运行

move底层

引用折叠

万能转发

sizeof(share_ptr)=?

内存对齐

        元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。元素放置的位置一定会在自己宽度的整数倍上开始,这就是所谓的内存对齐。

        内存对齐原因、好处:
(1)平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
(2)性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

        内存对齐原则:
(1)对齐参数是取每个变量自身对齐参数和系统默认对齐参数#pragma pack(n)中较小的一个。
(2)结构体中所有变量的对齐参数的最大值和系统默认对齐参数#pragma pack(n)比较,较小者作为对齐参数。

#pragma pack(n)的默认值为8

=》D 16;E 40

函数

        线程毕竟是比较贴近系统的东西,使用起来仍然不是很方便,特别是线程同步及获取线程运行结果上就更加麻烦。我们不能简单的通过 thread.join() 得到结果,必须定义一个线程共享的变量来传递结果,同时还要考虑线程间的互斥问题。好在 C++11 中提供了一个相对简单的异步接口 std::async ,通过这个接口可以简单的创建线程并通过 std::future 中获取结果。异步操作还未开始;ready:异步操作已经完成;timeout:异步操作超时。

        std::promise 是C++11并发编程中常用的一个类,常配合std::future使用。其作用是在一个线程t1中保存一个类型typename T的值,可供相绑定的std::future对象在另一线程t2中获取。

多线程同步互斥

创建一个安全协议

多线程编程打印100

实现一个socket

vector中的clear释放完所有空间了,如何释放完所有空间

迭代失效

coredump,想看堆栈信息,产生的原因

动态库什么时候会更改,什么时候要重新编译可执行文件

RPC

参考:二叉排序树的时间复杂度_heydyli的博客-CSDN博客

c++11 lambda表达式底层_lambdaquerywrapper底层_Forest_1010的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值