面向对象编程,将数据和函数放在一个类里面,但是STL将数据(容器)和函数(算法)进行了分离,所以两者在基础观念上就不一样。
序列容器:
关联容器:
C++ STL标准库使用模板编程完成的
函数模板:一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表,此通用函数即为函数模板
模板函数:为函数模板传参,根据实际传入的参数类型生成的一个重载函数即为模板函数,它是函数模板的一次实例化。
当函数重载与模板函数冲突的时候,优先调用普通函数,因为普通函数在编译期间就生成了,模板函数是运行时生成的,先到先得。
#include
using std::cout;
using std::endl;
// 函数模板:交换两个变量
template<typename T>
void MySwap(T &a,T &b){
T temp = a;
a=b;
b= temp;
}
template<typename T>
void MyPrint(T &a,T &b){
cout<<"a = "<<a << "b = "<< b<<endl;
}
int main(){
int a = 10;
int b = 20;
double aa = 10.2;
double bb = 20.1;
MyPrint(a,b);
MyPrint(aa,bb);
MySwap(a,b);
MySwap(aa,bb);
cout<<"======已经完成交换======"<<endl;
MyPrint(a,b);
MyPrint(aa,bb);
return 0;
}
#include
#include
using std::cout;
using std::endl;
using std::ostream;
class Complex{
double real;
double image;
public:
Complex(double real,double image):real(real),image(image){}
Complex operator+(const Complex& other){
return Complex(this->real+other.real, this->image+other.image);
}
friend ostream& operator<<(ostream& os, const Complex& p); //声明operator<<为友元函数,可以访问私有成员
};
ostream& operator<<(ostream &cout,const Complex& other){
cout<<other.real << "," <<other.image<<"i";
return cout;
}
int main(){
Complex c1(1.0f,2.2f);
Complex c2(2.0f,3.2f);
Complex c3=c1+c2;
cout<<c3<<endl;
return 0;
}
移动语义能提高复制对象性能
:移动构造在进行复制构造的时候,只需要把指针指向原数据,然后把原数据的指针置空,再设置数据大小。相比复制构造要去动态分配内存、再递归调用每个元素的复制构造函数的速度快得多。
右值引用是C++语法层面表示移动语义的机制:std::move()可以把任意类型转为右值引用,右值引用不过是另一个类型,没有那么神秘,当参数类型为右值引用时,函数移动它的资源,即为“移动语义”。在汇编代码的层面来看,左值或右值引用的representation都是一个内存地址,如同指针一般;而在C++语法层面,通过区分两种引用类型,程序员能更好地利用右值。
std::vector<int> vec_orange = { 1, 42, 23, 7, 13 };
std::vector<int> vec_red = std::move(vec_orange); //移动
移动语义意味着把对象持有的资源或内容转移给另一个对象。
class my_vector
{
int* data_;
size_t size_;
size_t capacity_;
public:
// 复制构造函数
my_vector(const my_vector& oth) :
size_(oth.size_),
capacity_(oth.capacity_)
{
data_ = static_cast<int*>(malloc(sizeof(int) * size_));
for (size_t i = 0; i < size_; ++i) {
data_[i] = oth.data_[i];
}
}
// 移动构造函数
// std::exchange(obj, new_val)的作用是返回obj的旧值,并把new_val赋值给obj
// 在移动oth的内容后,要把它置于一个有效状态。
my_vector(my_vector&& oth) :
data_(std::exchange(oth.data_, nullptr)),
size_(std::exchange(oth.size_, 0)),
capacity_(std::exchange(oth.capacity_, 0))
{}
};
C++智能指针std::unique_ptr,不可复制,只能移动。
template <typename T>
class my_unique_ptr
{
T* ptr_;
public:
// 移动构造函数
my_unique_ptr(my_unique_ptr&& oth) :
ptr_(std::exchange(oth.ptr_, nullptr)) {}
// 移动赋值函数
my_unique_ptr& operator=(my_unique_ptr&& rhs)
{
if (this != &rhs) {
if (ptr_) { // 释放当前拥有的指针
delete ptr_;
}
ptr_ = std::exchange(rhs.ptr_, nullptr); // 夺取rhs的指针
}
return *this;
}
// 禁用复制构造函数、复制赋值函数
my_unique_ptr(const my_unique_ptr&) = delete;
my_unique_ptr& operator=(const my_unique_ptr&) = delete;
};
下面的代码可以单独打印数组和链表
// 实现一个简单的双向循环List
template<typename T>
struct List_Node{
List_Node<T>* prev;
List_Node<T>* next;
T val;
};
template <typename T>
struct MyList{
typedef List_Node<T>* pointer;
MyList(){
node = new List_Node<T>;
node->next = node;
node->prev = node;
}
pointer begin(){
return node->next;
}
pointer end(){
return node;
}
void insert(pointer position, T val){
pointer tmp = new List_Node<T>;
tmp->val = val;
tmp->next = position->next;
tmp->prev = position;
position->next->prev = tmp;
position->next = tmp;
}
void erase(pointer position){
position->next->prev = position->prev;
position->prev->next = position->next;
delete position;
}
void push_back(T val){
insert(node->prev, val);
}
void pop_back(){
erase(node->prev);
}
private:
pointer node; //双向循环链表,代表链表尾节点
};
template <typename T>
void printVec(T begin, T end){
while(begin!=end){
cout<<*begin<<endl;
begin++;
}
}
template <typename T>
void printList(T begin, T end){
while(begin!=end){
cout<<begin->val<<endl;
begin=begin->next;
}
}
int main(){
int v[5] = {1,2,3,4,5};
printVec(v,v+5);
MyList<int> list;
list.push_back(10);
list.push_back(10);
list.push_back(20);
list.pop_back();
printList(list.begin(),list.end());
list.push_back(20);
printList(list.begin(),list.end());
return 0;
}
想要一个函数既能打印数组,又能打印链表,这个时候就要用到迭代器
迭代器就是连接容器和算法的桥梁: 1.链表和数组就是容器 2.打印函数就可以理解为算法。他们的连接方式是不同的。迭代器就在它们中间加了一个中间层。
template<typename T>
struct List_Node{
List_Node<T>* prev;
List_Node<T>* next;
T val;
};
template <typename T>
struct List_iterator{
typedef List_iterator<T> iterator;
typedef T value;
typedef T& reference;
List_Node<T>* node;
List_iterator():node(nullptr){}
List_iterator(List_Node<T>* node):node(node){}
// 前缀++ ++i
iterator& operator++() {
node = node->next;
return *this;
}
// 后缀++ i++ 不能加引用
iterator operator++(int) {
iterator tmp=*this;
node = node->next;
return tmp;
}
iterator& operator--() {
node = node->prev;
return *this;
}
iterator& operator--(int) {
iterator tmp=*this;
node = node->prev;
return tmp;
}
bool operator== (iterator& other){
return this->node == other->node;
}
bool operator!= (iterator& other){
return this->node != other->node;
}
iterator* operator-> (){
return this;
}
reference operator*(){
return node->val;
}
};
// 实现一个简单的双向循环List
template <typename T>
struct MyList{
typedef List_Node<T>* pointer;
typedef List_iterator<T> iterator;
MyList(){
node = new List_Node<T>;
node->next = node;
node->prev = node;
}
iterator begin(){
return node->next;
}
iterator end(){
return node;
}
void insert(iterator position, T val){
pointer tmp = new List_Node<T>;
tmp->val = val;
tmp->next = position.node->next;
tmp->prev = position.node;
position.node->next->prev = tmp;
position.node->next = tmp;
}
void erase(iterator position){
position.node->next->prev = position.node->prev;
position.node->prev->next = position.node->next;
delete position.node;
}
void push_back(T val){
insert(--end(), val);
}
void pop_back(){
erase(--end());
}
private:
pointer node; //双向循环链表,代表链表尾节点
};
template <typename T>
void printContainer(T begin, T end){
while(begin!=end){
cout<<*begin<<endl;
++begin;
}
}
int main(){
int v[5] = {1,2,3,4,5};
printContainer(v,v+5);
MyList<int> list;
list.push_back(10);
list.push_back(10);
list.push_back(20);
list.pop_back();
printContainer(list.begin(),list.end());
list.push_back(20);
printContainer(list.begin(),list.end());
return 0;
}
vector<int>:: iterator
sort(vec.begin(), vec.end()); // 怎么判断数据类型?
typedef ptrdiff_t difference_type;
typedef std::bidirectional_iterator_tag iterator_category;
typedef _Tp value_type;
typedef _Tp* pointer;
typedef _Tp& reference;
iterator_category:迭代器的分类,指它的移动性质,表示迭代器的类型(如输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器等)。这个信息可以方便算法采用最佳的移动方式。
value_type:迭代器指向的值类型。告诉算法是int 还是 string?
difference_type:迭代器之间的距离类型,通常使用ptrdiff_t。是inter?还是
pointer:指向value_type的指针类型。
reference:value_type的引用类型。
vector插入的方法:C++11之后,会直接调用emplace_back();
vector<int> vec;
vec.push_back(1); // 运行到下面
void
push_back(const value_type& __x) //添加左值时,这个函数会被调用,
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
__x); //_Alloc_traits::construct完成的,它通常会调用元素类型的复制构造函数。
++this->_M_impl._M_finish;
}
else
_M_realloc_insert(end(), __x);
}
#if __cplusplus >= 201103L //C++11及以后的版本,push_back提供了一个接受右值引用参数的重载版本。
void
push_back(value_type&& __x)
{ emplace_back(std::move(__x)); }
//push_back会调用emplace_back,并将右值引用传递给它。在emplace_back内部,元素会被直接在vector的内存中构造,使用的是元素类型的移动构造函数。
//对于可移动的对象,可以避免复制构造函数的调用,而是利用移动构造函数来构造新元素,通常这会更高效,因为它可以转移资源而不是复制资源。
}
emplace_back代码:
vector<_Tp, _Alloc>::
emplace_back(_Args&&... __args)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
std::forward<_Args>(__args)...);
++this->_M_impl._M_finish;
}
else
_M_realloc_insert(end(), std::forward<_Args>(__args)...);
可以看到这里在元素个数小于vector容量的时候,直接在尾部进行插入,否则就会调用realloc进行扩容。
而 _M_realloc_insert中会调用_M_check_len函数进行容量计算:
const size_type __len =_M_check_len(size_type(1), "vector::_M_realloc_insert");
size_type
_M_check_len(size_type __n, const char* __s) const
{
if (max_size() - size() < __n)
__throw_length_error(__N(__s));
const size_type __len = size() + std::max(size(), __n); // 这里就是若扩容两倍还不够,就把插入的内容大小加上原来大小。
return (__len < size() || __len > max_size()) ? max_size() : __len;
}
扩容之后进行拷贝
vector的扩容机制:
vector的迭代器就是一个指针。
每个节点都有颜色,非黑即红。
根节点为黑。
父子两节点不能同时为红。
任意节点到NULL的任何路径,所含黑节点数必须相同。
typedef _Rb_tree<key_type, value_type, _Identity<value_type>,
key_compare, _Key_alloc_type> _Rep_type;
_Rep_type _M_t; // Red-black tree representing set.
参考列表
https://www.bilibili.com/video/BV1Ga41177x8
https://zhuanlan.zhihu.com/p/347977300
https://www.bilibili.com/video/BV1pG4y197GW