C++说爱你不容易-7

C++软件与嵌入式软件面经解析大全(蒋豆芽的秋招打怪之旅)


本章讲解点

  • 1.1 C++与C的区别——看看你的理解是否深刻
  • 1.2 从代码到可执行文件的过程
  • 1.3 extern "C"
  • 1.4 宏——到底是什么
  • 1.5 内联函数
  • 1.6 条件编译
  • 1.7 字节对齐详解
  • 1.8 Const——今天必须把它搞懂
  • 1.9 Static作用
  • 1.10 volatile和mutable
  • 1.11 volatile在嵌入式里的应用
  • 1.12 原子操作
  • 1.13 指针与引用的区别
  • 1.14 右值引用
  • 1.15 面向对象的编程思想
  • 1.16 类
  • 1.17 类的成员
  • 1.18 友元函数
  • 1.19 初始化列表
  • 1.20 this指针
  • 1.21 继承
  • 1.22 多态
  • 1.23 虚函数与重写
  • 1.24 虚构造函数与虚析构函数
  • 1.25 函数重载
  • 1.26 操作符重载
  • 1.27 迭代器与指针
  • 1.28 模板
  • 1.29 C++智能指针
  • 1.30 四种cast转换
  • 1.31 Lambda
  • 1.32 function和bind

受众:本教程适合于C/C++已经入门的学生或人士,有一定的编程基础。

本教程适合于互联网嵌入式软件求职的学生或人士。

img

故事背景

img

蒋 豆 芽:小名豆芽,芳龄十八,蜀中人氏。卑微小硕一枚,科研领域苟延残喘,研究的是如何炒好一盘豆芽。与大多数人一样,学习道路永无止境,间歇性踌躇满志,持续性混吃等死。会点编程,对了,是面对对象的那种。不知不觉研二到找工作的时候了,同时还在忙论文,豆芽都秃了,不过豆芽也没头发啊。

隔壁老李:大名老李,蒋豆芽的好朋友,技术高手,代码女神。给了蒋豆芽不少的人生指导意见。

导 师:蒋豆芽的老板,研究的课题是每天对豆芽嘘寒问暖。

img

故事引入

img

蒋 豆 芽:老李,快点快点,我迫不及待想知道最后一个智能指针有什么用。

隔壁老李:豆芽,别急,你干嘛这么急?

蒋 豆 芽:我要去看我小侄女,我小侄女好可爱的,才一岁多,小豆芽成豆芽叔叔了,哈哈。

隔壁老李:那我们赶紧讲吧,讲完一起去看看侄女,哈哈!

img

1.29 智能指针

img

隔壁老李:豆芽,我们上一章讲了共享指针,很方便地实现了多个对象指向同一个资源。但是共享指针也存在内存泄露的问题。

我们来看看下面的代码。

#include <iostream>    
#include <memory>    
using namespace std;    

class B;  
class A{  
public:  
    shared_ptr<B> psb;  
    ~A(){ cout << "A delete\n"; }  
};  

class B{  
public:  
    shared_ptr<A> psa;  
    ~B(){ cout << "B delete\n"; }  
};  

int main(){    
    shared_ptr<B> pb(new B());  
    shared_ptr<A> pa(new A());  
    pb->psa = pa;  
    pa->psb = pb;  
    cout << pb.use_count() << endl;  
    cout << pa.use_count() << endl;  
    return 0;  
}  

运行代码如下:

2
2

可以看到主函数中,pa,pb之间相互引用,两个资源的引用计数为2,当跳出函数时,智能指针pa、pb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致资源并没有释放。这就是共享指针的循环引用计数问题。

蒋 豆 芽:这样啊,所以weak_ptr指针登场了对吧!

隔壁老李:没错。weak_ptr是弱引用,weak_ptr的构造和析构不会引起引用计数的增加或减少。我们可以将其中一个改为weak_ptr指针就可以了。比如我们将class B里shared_ptr换成weak_ptr,如下:

class B{  
public:  
    weak_ptr<A> psa;  
    ~B(){ cout << "B delete\n"; }  
};  

代码运行如下:

2
1
A delete
B delete

蒋 豆 芽:bingo!这次我懂了。所以四种智能指针auto_ptr (C++11被弃用)、unique_ptr、shared_ptr、weak_ptr,最常用的就是unique_ptrshared_ptr了,我可得好好掌握,面试很常考。

img

1.30 类型转换

img

隔壁老李:是的。好了,我们接下来讲类型转换。在讲之前我们又要复习下C语言中的类型转换。

蒋 豆 芽:你别说了,一个眼神我就懂,我马上给你回顾一下,老工具人了我。在一个运算表达式中,往往存在自动类型转换的情况。例如:设变量a的类型是char,变量b的类型是int,变量d的类型是double,求解表达式a+b-d。运算次序是:先计算a+b,将a转换为int型后求和,结果是int型;再将a+b的和转换为double型,再与d相减,结果是double型。

(1)自动类型转换记住一个图就可以了。

img

(2)强制转换

在下面代码中,

int sum = 7;  
double mean = sum / 5;

结果却是1.0,因为sumint型,sum / 5,小数点后面的数字会丢失,结果是1,再转为double,最后是1.0。出现了精度丢失的问题。

强制类型转换是把变量从一种类型转换为另一种数据类型。这时对int类型的sum强制转换为double类型。最后结果就是1.4了。

int sum = 17;
double mean = (double) sum/5;

隔壁老李:(笑容邪魅)好的,谢谢我们的工具人豆芽君。C++中,存在很多的类型转换,类间的上下转换,const与非const转换,空与非空转换。

因此C++为了将强制类型转换变得更加明确、控制强制转换的过程,主要将强制转换细化为四种cast转换方式:const_caststatic_castdynamic_castreinterpret_cast。我们一一讲解。

隔壁老李:四种cast转换格式均为

xxxx_cast<类型说明符> (变量或表达式)
  1. const_cast

    const_cast用于强制去掉不能被修改的常数特性,但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针引用的常量性,其去除常量性的对象必须为指针或引用。

    常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象。这句话什么意思呢?我们举个例子说明。

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <string>  
    #include <iostream>  
    using namespace std;  
    int main() {  
        const double a = 7;  
        const double* p = &a;  
        double* q = const_cast<double*>(p);  
        *q = 20; //通过q写值是未定义的行为  
        cout << "a=" << a << endl;  
        cout << "*p=" << *p << endl;  
        cout << "*q=" << *q << endl;  
        system("Pause");      
        return 0;  
    }  

    运行结果如下:

    a=7
    *p=20
    *q=20

    可以很清楚的看到,const变量*p被转换为非const变量*q,改变*q的值,const变量的值均发生了改变常量指针被转化成非常量指针,并且仍然指向原来的对象,就是这个意思。

    这样的操作语法没有问题,但是逻辑上却是有问题的,之前在C语言中我们讲过,明明是const常量,值却在中途发生了改变。所以我们要避免这样的操作,const_cast尽量少用。

  2. static_cast

    static_cast用于将一种数据类型强制转换为另一种数据类型。什么都可以转,最常用。如下:

    int a = 7;  
    int b = 3;  
    double result = static_cast<double>(a) / static_cast<double>(b); 

    但是在进行下行转换(把基类的指针或引用转换为派生类表示)时,由于没有动态类型检查,是不安全的。

    向上转换:指子类向基类转换。

    向下转换:指基类向子类转换。

    这两种转换,因为子类包含父类,子类转父类是可以任意转的。但是当父类转换成子类时可能出现非法内存访问的问题。

    img
  3. dynamic_cast

    正因为static_cast从基类向子类转换时不安全,所以又引入了dynamic_cast。dynamic_cast只能用于含有虚函数的类转换,用于类向上和向下转换。

    dynamic_cast通过判断变量运行时类型要转换的类型是否相同来判断是否能够进行向下转换。dynamic_cast可以做类之间上下转换,转换的时候会进行类型检查,类型相等成功转换,类型不等转换失败。

    运用RTTI技术,RTTI是“Runtime Type Information”的缩写,意思是运行时类型信息,它提供了运行时确定对象类型的方法。在c++层面主要体现在dynamic_cast和typeid,vs中虚函数表的-1位置存放了指向type_info的指针,对于存在虚函数的类型,dynamic_cast和typeid都会去查询type_info

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <string>  
    #include <iostream>  
    using namespace std;  
    class Base{  
    public:  
        Base() {};  
        virt

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

<p> - 本专刊适合于C/C++已经入门的学生或人士,有一定的编程基础。 - 本专刊适合于互联网C++软件开发、嵌入式软件求职的学生或人士。 - 本专刊囊括了C语言、C++、操作系统、计算机网络、嵌入式、算法与数据结构等一系列知识点的讲解,并且最后总结出了高频面试考点(附有答案)共近400道,知识点讲解全面。不仅如此,教程还讲解了简历制作、笔试面试准备、面试技巧等内容。 </p> <p> <br /> </p>

全部评论
最后一句话,placeholders为占位符的意思,用于函数绑定的时候,缺省函数的占位符 说了两遍
点赞 回复 分享
发布于 2023-04-02 21:04 甘肃
请问豆芽大佬,auto sumup = [total](int x)mutable{total += x;}; // total is:0,为啥total是0啊
点赞 回复 分享
发布于 2022-03-17 13:54
请问,const_cast代码示例那个地方,为啥 *q = 20不会改变a的值嘞?
点赞 回复 分享
发布于 2021-07-22 17:08
所以sizeof(sumup)就是sizeof(&total)了呀,sizeof(&total)=8 这里怎么是8呀??
点赞 回复 分享
发布于 2021-04-19 20:41

相关推荐

想进开水团喝开水:哦 给我一个 就算你真拿到牛友也会为你开心的
点赞 评论 收藏
分享
评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务