C/C++泛型编程

目录

1. 模板

1.0 函数模板

1.1 类模板

2. 顺序容器

2.0 vector容器

2.1 list容器

2.2 deque容器

2.3 string容器

3. 容器适配器

4. 关联容器

5. 智能指针

5.0 独享指针unique_ptr

5.1 共享指针shared_ptr

5.2 弱指针weak_ptr

5.3 环状引用问题


1. 模板

1.0 函数模板

样式示例如下

template
void Test(const T &t)
{
    T t1 = t;
    cout << t1 << endl;
}

1.1 类模板

样式示例如下

template
class Test
{
private:
    T * _data;
    public:
Test(int size)
    {
        _data = new T[size];
    }
};

2. 顺序容器

2.0 vector容器

vector容器在堆空间建立一个一维的数组,地址空间时连续的,在vector中删除和插入元素时效率低,原因是在删除或插入时后面的元素会向前或向后移动。

int main()
{
    vector v1;
    vector v2(10);            //指定大小
    vector v3(10, -1);       //指定大小和元素初始值
}

 vector相关函数的使用

int main()
{
    vector v;                        //定义
    for (int i = 0; i < 10;i++)
    {
        v.push_back(i + 1);            //添加
        cout << v.size() << '\t' << v.capacity() << endl;        //size函数和capacity函数
    }
    for (vector::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << endl;        //迭代器访问
    }
    v.resize(5);            //强制调整元素大小(个数)
    cout << v.size() << '\t' << v.capacity()  << endl;  //此时size : 5 ,capacity : 13
    for (vector::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << endl;            //只能访问到了前5个元素
    }
    v.reserve(100);                //手动改变空间大小
    cout << v.size() << '\t' << v.capacity() << endl;  //此时size : 5 ,capacity : 100
    v.shrink_to_fit();            //回收未使用空间
    cout << v.size() << '\t' << v.capacity() << endl;  //此时size : 5 ,capacity : 5

}

2.1 list容器

本质是一个双向链表,支持快速的插入和删除,不支持快速访问,只能从表头开始访问。

list与vector操作上大同小异,只不过其底层实现不同而已,不过list中并没有capacity的概念,所以没有其相关函数,示例如下

int main()
{
    list v;                        //定义
    for (int i = 0; i < 10;i++)
    {
        v.push_back(i + 1);            //添加
        cout << v.size() << '\t'  << endl;        //size函数
    }
    for (list::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << endl;        //迭代器访问
    }
    v.resize(5);            //强制调整元素大小(个数)
    cout << v.size() << '\t' << endl;  //此时size : 5
    for (list::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << endl;            //只能访问到了前5个元素
    }

}

2.2 deque容器

deque容器本质是一种双端队列,看狭义的看成是线性结构,所谓双端是指在队列头和队列尾都可进行删除和插入操作。

int main()
{
    deque v;                        //定义
    for (int i = 0; i < 5;i++)
        v.push_front(i + 1);            //对头插入
    for (int i = 5; i < 10;i++)
        v.push_back(i + 1);            //对尾插入
    //经过上述两步插入 此时队列中数据呈现这样的形态: [5,4,3,2,1,6,7,8,9,10]
    //注意队列是先进先出的线性结构,双端队列同样。
    for (deque::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << endl;        //迭代器访问
    }
    v.resize(5);            //强制调整元素大小(个数)
    cout << v.size() << '\t' << endl;  //此时size : 5
    for (deque::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << endl;            //只能访问到了前5个元素
    }
    v.shrink_to_fit();   //deque容器并未提供capacity函数,不过这无关紧要。
}

2.3 string容器

可以看成是专门处理char型的vector容器,其提供了丰富的字符串处理函数。

3. 容器适配器

容器适配器将原来的容器进行了一层封装。底层是普通容器,上层对外提供封装后的新接口。

常用的栈stack、队列queue和优先队列priority_queue在STL中都有自己的容器适配器。使用方法如下:

int main()
{
    //栈
    stack s;                                //默认使用deque
    stack> s1;            //指定使用vector
    //队列
    queue q;                                //默认使用deque
    queue> q1;            //指定使用list
    //优先队列
    priority_queue p;                //默认使用vector
    priority_queue> p1;        //指定使用deque
    priority_queue, cmp >> p2;    //指定使用vector和权重比较函数cmp
}

自定义优先队列的元素权重函数

class Square
{
public:
    int length;
    int width;
    Square(int l, int w):length(l),width(w){}
};

class Compare
{
public:
    bool operator() (Square x, Square y)
    {
        return ((x.length * x.width) < (y.length * y.width));
    }
};

int main()
{
    priority_queue, Compare > p;
    for (int i = 0; i < 10; i++)
        p.push(Square(rand() % 100 + 1, rand() % 100 + 1));

    while (!p.empty())
    {
        cout << p.top().length << "*" << p.top().width <<'\t'<<  p.top().length * p.top().width << endl;
        p.pop();
    }
}

4. 关联容器

STL中关联容器有四种:set、map、multiset和multimap.最常用的是前两种。

map中保存着一系列的键值对。键具有唯一性。可以使用下标访问符访问map中元素。map的下标是键,返回的是该键对应的值,也可以通过下标操作符给map添加元素。

map中元素是一系列键值对,每个键值对是pair类型。可以创建一个pair类型的变量并添加到map中,pair的第一个元素作为map元素的键,第二个元素作为map元素的值。可以通过first和second访问pair中的元素。具体操作如下

int main()
{
    pair p("sssss", 22);
    string s = p.first;
    int i = p.second;
    map m;
    m.insert(p);
    cout << m[s] << endl;
}

set中每个元素只包含一个键,并且set键具有唯一性

5. 智能指针

智能指针的本质是一个类模板,对普通的指针进行了一层封装,模板参数是指针指向的类型,通过重载->和*两个操作符使智能指针与普通指针用法相同。

C++常用的三个智能指针分别是:unique_ptr独享指针、shared_ptr共享指针和weak_ptr弱指针。

5.0 独享指针unique_ptr

独享指针unique_ptr唯一拥有所指向对象的所有权,不支持拷贝和赋值操作。因此不能用unique_ptr对另一个智能指针初始化或赋值,而只能通过move函数将其所有权转移给其他智能指针,确保不和其他智能指针指向同一个对象。

class Test
{
private:
    int _x;
public:
    Test(int t):_x(t) {}
    int getX() const { return _x; }
};

int main()
{
    unique_ptr p(new Test(12));
    cout << p->getX() << endl;
    //unique_ptr p1 = p;        //错误
    unique_ptr p2 = move(p);        //正确
    cout << p2->getX() << endl;
}

5.1 共享指针shared_ptr

作为最为常见的指针,多个shared_ptr共享所指向对象的所有权,通过引用计数器管理指向同意对象的指针个数。每增加一个智能指针指向对想,引用计数器就加一,当指向同意对象的所有智能指针生命周期全部结束时,引用计数器为0,此时释放对象空间。

class Test
{
private:
    int _x;
public:
    Test(int t):_x(t) {}
    int getX() const { return _x; }
    ~Test() { cout << "destory Test! " << endl; }
};

int main()
{
    shared_ptr p(new Test(12));
    shared_ptr p1 = p;
    cout << p->getX() << endl;                //输出_x值为12
    cout << p1.use_count() << endl;            //输出引用计数值为2
    p.reset();
    cout << p1.use_count() << endl;            //输出引用计数值为1
}

5.2 弱指针weak_ptr

弱指针一般配合共享指针一起使用,weak_ptr可以和shared_ptr指向同一个对象,但是不改变引用计数器的值。

5.3 环状引用问题

两个shared_ptr指针所指对象的数据成员中,如果含有指向对方对象的share_ptr指针,则会产生环状引用。

class B;
class A
{
public:
    shared_ptr bptr;
    ~A() { cout << "~ A ()" << endl; }
};
class B
{
public:
    shared_ptr aptr;
    ~B() { cout << "~B()" << endl; }
};

int main()
{
    shared_ptr
a(new A());
    shared_ptr b(new B());

    a->bptr = b;
    b->aptr = a;
}

程序中通过创建了一个类A的对象的一个类B的对想,并通过shared_ptr指向他们,之后通过指向另一个对象的智能指针对自身的数据成员ptr进行赋值,因此两个对象的引用计数器都为2.

当程序退出时,main函数创建的智能指针生命周期结束,其所指向的对象引用计数减1,由于环状引用,对象内部的生命周期都不会结束,两个对象引用计数始终未1.

具体来讲,对象A中的智能指针bptr只有在对象A析构之后才会结束,从而将对象B的引用计数减1,但对象A并不会销毁,因为A销毁的条件是指向A的智能指针计数为0,而对象B中指向A的指针只有在B析构之后销毁,这是一个逻辑上的死结。

解决的方法是应用weak_ptr, 重写代码如下:

class B;
class A
{
public:
    weak_ptr bptr;
    ~A() { cout << "~ A ()" << endl; }
};
class B
{
public:
    weak_ptr
aptr;
    ~B() { cout << "~B()" << endl; }
};

int main()
{
    shared_ptr
a(new A());
    shared_ptr b(new B());

    a->bptr = b;
    b->aptr = a;
}

 

 

你可能感兴趣的:(C/C++)