嵌入式八股文-----------------面向对象

目录

前言:

一、面向对象:

1.1 什么是面向对象:

1.2面向对象与面向过程的区别:

1.3三大特征:

1.4 深浅拷贝:

1.5 友元:

1.6 虚函数:

二、C++精华高级:

2.1 Vector底层实现:

2.2 vector和list的区别:

2.3 vector扩容:

2.4 迭代器:

2.5 删除元素导致两者迭代器发生什么变化

2.7 class 实现智能指针:

2.8 既生指针何生迭代器:

2.9  STL中迭代器是如何删除元素

2.9 map和set:

2.10 C++中如何阻止类被实例化:

2.11 纯虚函数

2.22 C++一些特殊情况:

三、总结:


前言:

        面向对象,C++是一门半面向对象半面向过程的语言,一般来说面向过程的东西要比面向对象的东西难,个人是这样理解的。所以C++是真的比较难学,也是真的比较好,钱途一片光明。(闪闪亮亮的),对象都还没找到就要去面向对象,有点好笑,不管了,早晚要面对的,来吧,接下来开始面向对象。

一、面向对象:

1.1 什么是面向对象:

        面向对象是种编程思想,把一切东西看成一个对象,这个对象拥有自己的属性,我们把对象拥有的属性变量和操作这些属性变量的函数打包成一个类来表示

1.2面向对象与面向过程的区别:

 面向过程:依据业务逻辑从上到下写代码

面向对象:将数据与函数绑定到一起,进行封装,对象理论上是不能直接操作数据,只能通过对 应的函数操作里面的数据,这样能够更快速的开发程序,减少了重复代码的重写过程

1.3三大特征:

 继承 、封装 、 多态

封装:

将数据和操作数据的方法有机结合,隐藏对象的属性和实现细节,
仅仅对外提供接口来和对象进行交互,封装保证了模块较好的独立性,
是的程序维护修改较为容易



继承:
对象的一个新类可以从现有的类中派生,这个就是继承,新类保留了原始类的特性,

新类叫做派生类,或叫子类,原始类叫父类或叫基类,继承很好的解决了代码可重用性问题,

在子类里面可以添加属于自己的新的函数或者变量,使得	更加符合需求


继承方式
    共有、私有、保护
    对于基类的私有成员无论子类是以那种方式继承都无法操作基类的私有成员

    对于私有继承,会把基类的保护成员、共有成员变为私有成员

    对于保护继承,会把基类的保护成员、共有成员变为保护成员

PS:        

      类中可以直接访问自己类的public、protected、private成员,但类对象只能访问自己类的public成员  

对于继承的话,子类和子类对象访问基类的成员也会受到继承方式影响:

对于共有继承,子类可以访问基类public、protected,子类对象可以访问基类的public成员

对于私有继承,子类可以访问基类public、protected,子类对象不可以访问基类的成员

对于保护继承,子类可以访问基类public、protected,子类对象不可以访问基类的成员

多态:

用父类的指针指向子类的实例,然后通过父类的指针调用子类的成员函数,
一般有重写、重载
重写是动态多态,重载是静态多态(编译器在编译期完成)
重写需要满足条件:

1)虚函数。基类中必须有虚函数,在派生类中必须重写虚函数。
2)通过基类类型的指针或引用来调用虚函数
1.4 深浅拷贝:
什么是深拷贝?什么是浅拷贝?
对一个已知对象进行拷贝,编译系统会自动调用一种构造函数——拷贝构造函数,

如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数

    编译系统在我们没有自己定义拷贝构造函数时,会在拷贝对象时调用默认拷贝构造函数,
进行的是浅拷贝,浅拷贝(释放时,因为多次释放出错,
如果里面有指针就会出现不同对象对同一个内存进行是否)

在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,
使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存出现两次释放发生

总结:
浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,
深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针

PS:

如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝;  如果B没变,那就是深拷贝!

1.5 友元:
友元有两种形式:

友元函数:友元函数的声明,声明位置没有关系,

友元类

作用:类中的有些成员只能通过类提供的函数进行访问,这会增加程序书写的麻烦,
所以就搞了个友元friend,通过友元就可以访问类中的私有和保护成员

缺点:破坏了类的封装性和数据的透明性
1.6 虚函数:
什么函数不能声明为虚函数?

普通函数(非成员函数),静态成员函数,内联成员函数,构造函数,友元函数

二、C++精华高级:

2.1 Vector底层实现:

vector底层实际是动态类型顺序表,因此其底层实际是一段连续的空间(底层是数组),实际在底层使用三个指针指向该段连续空间的。start指向空间的起始位置,finish指向最后一个元素的下一个位置,end_of_storage指向空间的末尾。

//部分源码
template <class T, class Alloc = alloc>
class vector {
public:
  typedef T value_type;           // vector中元素的类型
  typedef value_type* iterator;   // vector的迭代器,实际就是原生态的指针的别名
  // ...
protected:
  iterator start;                 // 指向底层空空间的起始位置
  iterator finish;                // 指向最后一个有效元素的下一个位置,没有元素时与start在同一位置
  iterator end_of_storage;        // 指向空间的末尾
  
  // ... 
  // 开辟n个元素的一段连续空间,并使用value来进行填充
  void fill_initialize(size_type n, const T& value) {
    start = allocate_and_fill(n, value);
    finish = start + n;
    end_of_storage = finish;
  }
  
  // ...
  
public:
  // n向vector中填充n个位置value的元素
  vector(int n, const T& value) { fill_initialize(n, value); }
  // ...
 
 public:
  // vector的迭代器
  iterator begin(){ return start; }
  iterator end(){ return finish; }
  
  // ...
  
};
2.2 vector和list的区别:
Vector底层是数组,List是双链表
Vector支持随机访问,list不支持
Vector是顺序内存
Vecotr随机访问性能好,插入删除性能差,list相反
vecor一次性分配好内存,不够才2被进行扩容,List每次插入一个节点都会进行内存申请
2.3 vector扩容:
10.vector扩容时发生了什么?

vector空间已满时会申请新的空间并将原容器中的内容拷贝到新空间中,

并销毁原容器存储空间的重新分配会导致迭代器失效
2.4 迭代器:
1.容器的迭代器时由什么组成的:

迭代器是 STL 中的一种基本数据类型,它是一种抽象的指针,用于遍历容器中的元素。
迭代器由指针和操作符组成,其中指针指向容器中的元素,操作符用于对指针进行操作,
例如移动指针、解引用指针等。迭代器的种类有很多,
包括输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器等。
不同种类的迭代器支持的操作也不同,例如输入迭代器只支持解引用和后移操作,
而随机访问迭代器则支持解引用、后移、前移、加法、减法等操作。在使用迭代器时,
需要根据容器的类型和需要进行的操作选择合适的迭代器类型。

举个迭代器使用例子:

#include <iostream>
#include <vector>

using namespace std;

int main() {
    vector<int> vec = {1, 2, 3, 4, 5};
    vector<int>::iterator it;
    for (it = vec.begin(); it != vec.end(); it++) {
        cout << *it << " ";
    }
    return 0;
}


2.如何理解迭代器

迭代器类型主要支持两类,随机访问和双向访问。

其中vector和deque支持随机访问,list,set,map等支持双向访问

2.5 删除元素导致两者迭代器发生什么变化
1) 对于关联容器(如map, set,multimap,multiset),删除当前的iterator,
仅仅会使当前的iterator失效,只要在erase时,递增当前iterator即可。这是因为map之类的容器,
使用了红黑树来实现,插入、删除一个结点不会对其他结点造成影响。

2)对于序列式容器(如vector,deque),删除当前的iterator会使后面所有元素的iterator都失效。
这是因为vetor,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置。
还好erase方法可以返回下一个有效的iterator。

3)对于list来说,它使用了不连续分配的内存,并且它的erase方法也会返回下一个有效的iterator,
因此上面两种正确的方法都可以使用。
2.7 class 实现智能指针:
为了实现一个智能指针,我们可以创建一个类来管理指针。
该类应该包含一个指向所管理对象的指针,以及一个计数器来跟踪有多少个智能指针正在管理该对象。
当计数器为零时,该对象应该被删除。我们可以使用引用计数技术来实现这一点。
每当创建一个新的智能指针时,计数器就会增加1,每当销毁一个智能指针时,计数器就会减少1。
当计数器为零时,我们就知道没有智能指针在管理该对象,因此可以安全地删除该对象。
(类似FreeRTOS中的信号量)


简单示例:
template <typename T>
class SmartPtr {
public:
    SmartPtr(T* ptr = nullptr) : m_ptr(ptr) {
        if (m_ptr) {
            m_refCount = new size_t(1);
        }
        else {
            m_refCount = new size_t(0);
        }
    }

    SmartPtr(const SmartPtr<T>& other) {
        m_ptr = other.m_ptr;
        m_refCount = other.m_refCount;
        (*m_refCount)++;
    }

    SmartPtr<T>& operator=(const SmartPtr<T>& other) {
        if (this != &other) {
            if (m_ptr) {
                (*m_refCount)--;
                if (*m_refCount == 0) {
                    delete m_ptr;
                    delete m_refCount;
                }
            }
            m_ptr = other.m_ptr;
            m_refCount = other.m_refCount;
            (*m_refCount)++;
        }
        return *this;
    }

    ~SmartPtr() {
        (*m_refCount)--;
        if (*m_refCount == 0) {
            delete m_ptr;
            delete m_refCount;
        }
    }

    T& operator*() const {
        return *m_ptr;
    }

    T* operator->() const {
        return m_ptr;
    }

private:
    T* m_ptr;
    size_t* m_refCount;
};
2.8 既生指针何生迭代器:

2.9  STL中迭代器是如何删除元素
对于序列容器vector和dequeue,使用erase后,后面的元素的迭代器都会失效
,但是后面每个元素都会往前面移动一个位置,erase返回下一有效的迭代器
对于关联容器map.set使用erase后,当前的元素的迭代器会失效,但是其结构是红黑树,
删除当前元素不会影响到下一个元素的迭代器,所以在调用erase之前,记录下个元素的迭代器即可
对于list,使用不连续分配的内存,并且他的erase方法也会返回下个有效的迭代器
2.9 map和set:
map和set都是c++的关联容器,底层都是红黑树
区别:
map中的元素是键值对,set只是关键字的集合,所以set中元素只包含一个关键字
set的迭代器是const,不允许修改元素的值
map允许修改value,但不允许修改key
原因:
    根据关键字来保证器有序性,如果修改key的话,那么首先删除改建,
然后调节平衡,再依据修改后的建值,调节平衡,这样一来破坏map和set的结构,iterator失效



STL中的MAP数据如何存放的?

map是使用红黑树实现,unordered_map是使用hash表



STL中的map和unordered_map有什么区别?


map是使用红黑树实现,unordered_map是使用hash表来完成映射功能
map是按照operator<比较判断元素是否相同,及比较元素的大小,然后选择一个合适位置插入书中,
所以对map遍历的话是有序的
unordered_map是计算元素的hash值,根据hash的值判断元素是否相同,所以对unordered_map遍历是无序的



STL的resize和reserver的区别??
resize改变容器含有元素的数量,比如:resize(15),原来的大小是10,
那么使用resize之后就会增加5个为0的元素
reserver改变容器的最大容量capacity,不会生成元素,如果改变之后容器容量大于当前的capacity,
那么就会出现分配一个空间,把之前的元素全部盖被到新的空间中









2.10 C++中如何阻止类被实例化:

可以通过使用抽象类,或者将构造函数说声明为private,抽象类之所以能被实例化,是因为抽象类不能代表一类具体的事物,他是对多种相似的具体事物的共同特征的一中抽象

2.11 纯虚函数

纯虚函数指的是什么?

Class 类名

{

        virtual ()=0

};

含有纯虚函数的类称为抽象类,抽象类不能生成对象,纯虚函数永远不会被调用,他们呢主要是用来统一管理子类对象

2.22 C++一些特殊情况:
C++中哪些情况只能初始化列表,而不能赋值?

在c++中赋值就是删除原值,赋予新值,初始化列表是开辟空间和初始化同时完成
	1.类中的const ,reference(引用)成员变量时,只能初始化
	2.若成员类型是没有默认构造函数的类,只能使用初始化列表
    3.派生类在构造函数中要对自己的自身成员初始化,也要对继承过来的基类成员进行初始化,

当基类没有默认构造函数的时候,通过在派生类的构造函数初始化列表中调用基类的构造函数初始化


没有参数的函数能不能被重载?

可以

class A
{
public:
    void f()
    {
        cout << 1 << endl;
    }
    void f() const //没有参数的函数也是我可以被重载的,只不过要想使用,那么对象也必须是const
    {
        cout << 2 << endl;
    }
};

int main()
{
    A a;
    const A b; 
    a.f();  //1
    b.f();  //2
    return 0;
}





三、总结:

写的比价简答,毕竟只是大概的整合一下,里面的细节大家可以自己去找一下。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值