C/C++面试知识点总结之STL篇

目录

 

一、智能指针的实现

1、auto_ptr

2、shared_ptr

3、unique_ptr

4、weak_ptr

二、vector的原理及实现

三、deque的原理及实现

四、list的原理及实现

五、配置器、配接器、迭代器

六、仿函数


一、智能指针的实现

        C++程序设计中堆内存是一个非常频繁的操作,堆内存的申请和释放都由程序员自己管理,虽然自己管理内存提高了程序的效率,但是整体来说还是比较麻烦的。使用普通指针,忘了释放容易造成内存泄漏,二次释放、程序异常时造成内存泄漏,使用智能指针能更好的解决这个问题。实现原理:RAII(资源分配即初始化)

智能指针的四种类型:1、auto_ptr    2、shared_ptr    3、unique_ptr    4、weak_ptr

1、auto_ptr

(无论什么情况下都不要使用,C++98标准,到了C++11标注引入了shared_ptr,unique_ptr,,weak_ptr)

2、shared_ptr

       shared_ptr和auto_ptr最大的区别就是,shared_ptr解决了指针间共享对象所有权的问题,也就是auto_ptr中的赋值的奇怪问题。所以满足了容器的要求,可以用于容器中。而auto_ptr显然禁止共享对象所有权,不可以用于容器中。

成员函数:

(1)默认构造函数,函数参数为变量地址

(2)拷贝构造函数,函数参数为引用

(3)重载函数operator*

(4)重载函数operator->

(5)重载函数operator=,使之可以进行隐性转换操作,注:实际C++源码中是没有这个的,构造函数用关键字explicit声明,在定义对象时必须显示调用初始化式,不能使用赋值操作符进行隐式转换。

(6)空函数判断

(7)引用次数统计函数

注意:使用shared_ptr也要引用头文件#include<memory>,如下使用简单的源码实现

#ifndef _SHARED_PTR_H
#define _SHARED_PTR_H
/*
  一个模板T* ptr,指向实际的对象
  一个引用次数
  重载operator*和operator->,使得能像指针一样使用share_ptr
  重载copy constructer,使其引用次数加一
  重载operator=,如果原来的shared_ptr已经有对象
*/

template<typename T>
class shared_ptr
{
public:
	shared_ptr(T* p) :count(new int(1)), _ptr(p); //默认构造函数,必须自己显式的开辟内存
	shared_ptr(shared_ptr<T>& p) :count(&(++p.count)), _ptr(p._ptr); //拷贝构造函数,属于强制转换,显式

	T& operator*(); //
	T* operator->();
	shared_ptr<T> & operator=(shared_ptr<T>& p); //对等号进行重载,保证保存同为shared_ptr的指针能相互转换,等号
												 //左边计数器减1,右边计数器加1。
	~shared_ptr();
	bool empty();  //检查是否指向一个空T
	int GetCount();
private:
	int* count;  //引用计数器
	T* _ptr;   //每创建一个对象,则有一个指针指向一个shared_ptr类型 
};

#endif
#include"_shared_ptr.h"

template<typename T>
shared_ptr<T>::shared_ptr(T* p = nullptr) :count(new int(1)), _ptr(p) //默认构造函数,必须自己显式的开辟内存
{
	if (_ptr)
	{
		count = new int(1); //如果初始值不为空,则计数器为1
	}
	else
	{
		count = new int(0); //当初始值为空时,则计数器为0;
	}
} 

template<typename T>
shared_ptr<T>::shared_ptr(shared_ptr<T>& p) : count(&(++p.count)), _ptr(p._ptr) //拷贝构造函数,属于强制转换,显式
{
	if (this != p)
	{
		this->_ptr  = p._ptr;
		this->count = p.count;
		*(this->count)++;
	}
} 

template<typename T>
T& shared_ptr<T>::operator*()
{
	//assert(this->_ptr == nullptr)
	return *(this->_ptr);
}

template<typename T>
T* shared_ptr<T>::operator->()
{
	//assert(this->_ptr == nullptr);
	return this->_ptr;
}

template<typename T>
shared_ptr<T>& shared_ptr<T>::operator=(shared_ptr<T>& p) //对等号进行重载,保证保存同为shared_ptr的指针能
														  //相互转换,等号左边计数器减1,右边计数器加1。
{
	++*(p.count); //等式右边引用次数加一,左边引用次数减一
	if (this->_ptr && 0 == --*this->count) //当左边引用次数为零
	{
		delete count;
		delete _ptr;
	}
	this->count = p.count;
	this->_ptr = p._ptr;
	return *this;
}

template<typename T>
shared_ptr<T>::~shared_ptr() //当诶空时,清除
{
	if (0 == *(this->count))
	{
		delete count;
		delete _ptr;
	}
}

template<typename T>
bool shared_ptr<T>::empty() //检查是否指向一个空T,当为空时,记得count也为零
{
	return _ptr == nullptr;
}

template<typename T>
int shared_ptr<T>::GetCount()
{
	return *count;
}

3、unique_ptr

       unique_ptr的构造函数与auto_ptr一样,构造函数采用explicit声明,防止复制/拷贝时不必要的类型转换,在定义对象时必须显示调用初始化式,不能使用赋值操作符进行隐式转换。注:此代码未包含自定义删除器

成员函数:

(1)get函数:获取内部对象的指针,由于已经重载了()方法,因此和直接使用对象是一样的。

(2)release函数:放弃内部对象的所有权,将内部指针置空,此指针需要手动释放。

(3)reset函数:销毁内部对象并接受新的对象的所有权。

(4)默认构造函数,函数参数为变量地址

(5)拷贝构造函数,函数参数为引用

(6)重载函数operator*

(7)重载函数operator->

(8)重载函数operator=

#ifndef UNIQUE_PTR_H
#define UNIQUE_PTR_H

template<typename T>
class _unique_ptr
{
public:
	_unique_ptr(T* p = nullptr) :_ptr(p);        //默认构造函数
	_unique_ptr(_unique_ptr<T>& p) :_ptr(p._ptr); //拷贝构造函数

	T& operator*();
	T* operator->();
	_unique_ptr<T>& operator=(_unique_ptr<T>& p); //赋值操作符重载

	T* get();
	T* release();
	void reset(T* p);

private:
	T * _ptr;
};

#endif
#include"unique_ptr.h"

template<typename T>
_unique_ptr<T>::_unique_ptr(T* p)
{
	_ptr = p;
}

template<typename T>
_unique_ptr<T>::_unique_ptr(_unique_ptr<T>& p)
{
	_ptr = p.release();
}

template<typename T>
T& _unique_ptr<T>::operator*()
{
	return *(this->_ptr);
}

template<typename T>
T* _unique_ptr<T>::operator->()
{
	return this->_ptr;
}

template<typename T>
_unique_ptr<T>& _unique_ptr<T>::operator=(_unique_ptr<T>& p)
{
	if (p.get() != this->get())
	{
		delete _ptr;
	}
	_ptr = p._ptr;
}

template<typename T>
T* _unique_ptr<T>::get()
{
	return this->_ptr;
}

template<typename T>
T* _unique_ptr<T>::release()
{
	T* tmp = _ptr;
	delete _ptr;
	return tmp;
}

template<typename T>
void _unique_ptr<T>::reset(T* p)
{
	if (p != _ptr)
	{
		delete _ptr;
	}
	_ptr = p;
}

 

4、weak_ptr

       weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr。 weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。定义在 memory 文件中(非memory.h), 命名空间为 std.

二、vector的原理及实现

       原理:vector模塑出一个动态数组。它本身是“将元素置于动态数组中加以管理”的抽象概念。将元素复制到动态数组中,元素之间存在某种顺序,所以vector是一种有序群集,支持随机存储,它的迭代器是随机存取迭代器。vector的动态增长的三步骤为,开辟新空间,移动数据,销毁旧空间。需要注意的是,所谓的动态增长并不是在原空间之后分配接续的空间,而是另外分配大于原来空间两倍的新空间。

vector的操作函数

1、构造、拷贝和析构函数

vector<T>  c1 产生一个空的vector
vector<T>  c1(c2)产生一个同型vector副本
vector<T>  c1(n)产生一个大小为n的vector
vector<T>   c1(n,elem)产生一个大小为n,元素值为elem的vector
vector<T>   c1(begin,end)产生一个以区间[begin,end]做元素初值的vector
c1.vector<T>()销毁所有元素

2、非变动型操作

c.size()返回当前元素数量
c.empty()判断大小是否为零。
capacity()返回重新分配空间前所能容纳的元素最大数量
reverse()如果容量不足,扩大
c.max_size()返回可容纳的元素最大数

3、赋值

c1 = c2将c2的全部元素赋值给c1
c.assign(n,elem)复制n个elem,赋值给c
c.assign(begin,end)将区间[begin,end]内的元素赋值给c
c1.swap(c2)将c1和c2元素互换

4、元素存取

c.at(idx)返回引索为idx所变的元素
c[idx]返回引索为idx所变的元素
c.front()返回第一个元素。不检查第一个元素是否存在
c.back()返回最后一个元素。不检查最后一个元素是否存在

5、迭代器相关函数

c.begin()返回一个随机存取迭代器,指向第一个元素
c.rbegin()返回一个逆向迭代器,指向逆向迭代的第一个元素
c.end()返回一个随机存取迭代器,指向最后一个元素
c.rend()返回一个逆向迭代器,指向逆向迭代的最后一个元素

6、安插与删除

c.insert(pos,elem)在pos中插入一个elem副本,返回新元素位置
c.insert(pos,n,elem)在pos位置插入n个elem副本,无返回值
c.insert(pos,beg,end)在pos插入区间[beg;end]内的所有元素的副本,无返回值
c.push_back(elem)在尾部添加一个elem
c.pop_back()删除尾部的一个元素
c.erase(pos)删除pos位置的元素,并返回指向下一个元素的位置
c.erase(beg,end)删除区间[beg,end]所有元素,并指向下一个元素的位置
c.resize(num)将元素数量改为num
c.resize(num,elem)将元素数量改为num,多出来的新元素都是elem副本
c.clear()删除所有元素,将容器清空

异常处理:

(略)

三、deque的原理及实现

       容器deque和vector非常的相似,也是采用动态数组来管理元素,提供随机存储,并有和vector几乎一模一样的接口。不同的是deque的动态数组头尾都开放,因此能在头尾两端进行快速安插和删除。

四、list的原理及实现

list使用一个双向链表来管理元素,list的内部结构和vector或deque截然不同,以下在主要方面与前述二者存在明显区别:

  • list不支持随机存取
  • 任何位置执行元素的安插和删除都非常快,始终是在常数时间内完成
  • 对异常处理,要么成功,要么什么都不发生
  • 由于不支持随机存储,既不提供下标操作符,也不提供at()
  • 并未提供容量、空间重新分配等操作函数
  • 提供不少成员 函数用于移动函数。

五、配置器、配接器、迭代器

       配置器:C++标准库在许多地方采用特殊对象来处理内存配置和寻址,这样的对象称为配置器。配置器体现出一种特定的内存模型,成为一个抽象表征。C++标准程序库定义了一个缺省配置器如下:

namespace std
{
    template <class T>
    class allocator;
}

       缺省配置器可以在任何“配置器得以被当做参数使用”的地方担任默认值。缺省配置器会执行内存分配和回收的一般性手法,也就是调用new和delete操作符。配置器说白了就是一个内存分配器,使得像内存共享、垃圾回收、面向对象数据库等特定内存模型,保持一致的接口。

       配接器:一般只有一个私有成员变量的类,且其全部成员函数都是对该唯一的成员变量的存、取和修改,则该类即为对该私有成员变量的配接。将一个类的接口转换成另一个类的接口,使原本因接口不兼容而不能合作的类可以一起运作,即配接器用于改变接口

STL主要提供三种配接器:

  • 改变仿函数接口,functor adapter
  • 改变容器接口,container adapter
  • 改变迭代器接口,iterator adapter

(1)应用于仿函数:是所有配接器中数量最为庞大的一个种群,可以配接、配接、在配接,配接操作包括:(1)系结(2)否定(3)组合,以及对一般函数或成员函数的修饰。C++标准这些规定配接器的接口可由<functional>获得

(2)应用于容器:标准程序库提供的queue和stack,其实都不过是一种配接器,是对deque接口的修饰而成就自己的容器风貌,序列式容器set和map是对其内部所维护的平衡二叉树接口改造

(3)应用于迭代器:STL提供应用于迭代器身上的配接器,这些接口为<iterator>

       迭代器:迭代器是一个抽象概念,任何东西,只要器行为类似迭代器,都可以是一个迭代器。迭代器是一种“能够遍历某个序列内所有元素”的对象。它可以透过与一般指针一致的接口来完成自己的工作(侯捷,C++标准程序库)

六、仿函数

仿函数:所谓仿函数,是一个定义了operator()的对象,它不是函数,而是一个类,该类重载了()操作符

仿函数三大优点:

(1)仿函数比一般函数更灵巧,因为它可以拥有状态。

(2)每个仿函数都有其类型。

(3)执行速度上,仿函数通常比函数指针更快。

#include<iostream>
#include<string>
#include<set>
#include<algorithm>

using namespace std;

class Person
{
    public:
        string FirstName() const;
        string LastName() const;
};

class PersonSortCriterion
{
    public:
        bool operator() (const Person& p1,const Person& p2) const
        {
            return p1.LastName() < p2.LastName() ||
                   (!(p2.LastName() < p1.FirstName()) &&
                    p1.FirstName() < p2.FirstName());
        }
};

int main()
{
    set<Person,PersonSortCriterion> PersonSet;

    PersonSet coll;
    PersonSet::iterator pos;
    for(pos = coll.begin();pos != coll.end();++pos)
    {
        cout << *pos<< "  ";
    }
    /*
    C++11新特性auto和区间迭代
    for(auto itor:coll)
    {
        cout << itor << "  ";
    }
    cout << endl;
    */
    cout <<endl;
    system("pause");
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值