C++黑马程序员教程-学习笔记三

该博客主要记录C++提高编程的相关知识,包括模板(函数模板和类模板)和STL(标准模板库)。详细介绍了模板的基本语法、调用规则、与普通函数的区别等,还阐述了STL的容器(如vector、stack等)、函数对象和各类算法(遍历、查找、排序等)的特点和使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

三、提高编程

3.1 模板(范式编程)

3.1.1 函数模板

3.1.1.1 基本语法

3.1.1.2 普通函数与模板函数

3.1.1.2.1 区别

3.1.1.2.2. 调用规则

3.1.2 类模板

3.1.2.1 基本语法

3.1.2.2 类模板与函数模板区别

3.1.2.3 类模板中成员函数创建时机

3.1.2.4 类模板对象做函数参数

3.1.2.5 类模板与继承

3.1.2.6 类模板成员函数类外实现

3.1.2.7 类模板分文件编写

3.2 STL(standard template library,标准模板库)

3.2.1 容器

3.2.1.1 string 字符串

3.2.1.2 vector

3.2.1.3 deque 双端数组

3.2.1.4 stack 栈

3.2.1.5 queue 队列

3.2.1.6 list 链表

3.2.1.7 set

3.2.1.8 pair

3.2.1.9 map

3.2.2 函数对象(仿函数)

3.2.3 算法 algorithm

3.2.3.1 遍历算法 for_each

3.2.3.2 查找算法

3.2.3.2.1 find

3.2.3.2.2 binary_search

3.2.3.2.3 count

3.2.3.3 排序算法

3.2.3.3.1 sort

3.2.3.3.2 random_shuffle 随机打乱

3.2.3.3.3 merge 合并后排序

3.2.3.3.4 reverse 反转

3.2.3.4 拷贝和替换

3.2.3.4.1 copy

3.2.3.4.2 replace

3.2.3.4.3 swap

3.2.3.5 算术生成算法

3.2.3.5.1 accumulate 累加

3.2.3.5.2 fill 后期重新填充

3.2.3.6 集合算法

3.2.3.6.1 set_intersection 交集

3.2.3.6.2 set_union 并集

3.2.3.6.3 set_difference 差集


三、提高编程

3.1 模板(范式编程)

3.1.1 函数模板

3.1.1.1 基本语法
template<typename T>    //T可以匹配任何类型int/float...
void mySwap(T &a, T&b)
{
    T temp = a;
    a = b;
    b = temp;
}

int main()
{
    int a = 10;
    int b = 20;

    //第1种写法:系统自动判断类型
    mySwap(a, b);
    //第2种写法:显示指定类型
    mySwap<int>(a, b);
    cout << b << ' ' << a << endl;
}
3.1.1.2 普通函数与模板函数
3.1.1.2.1 区别

普通函数会发生隐式类型转换,模板函数(依赖系统自动判断类型)不会发生隐式类型转换,模板函数(显示指定类型)会发生隐式类型转换,因此推荐使用后者方法2。

3.1.1.2.2. 调用规则

1.如果函数模板和普通函数都可以实现,优先调用普通函数
2.可以通过空模板参数列表来强制调用函数模板
3.函数模板也可以发生重载
4.如果函数模板可以产生更好的匹配,优先调用函数模板

3.1.2 类模板

3.1.2.1 基本语法
template<class NAMETYPE, class AGETYPE>
class Person
{
public:
    Person(NAMETYPE name, AGETYPE age)
    {
        this->m_name = name;
        this->m_age = age;
    }

    void showPerson()
    {
        cout << "name: " << this->m_name << " age: " << this->m_age << endl;
    }

private:
    NAMETYPE m_name;
    AGETYPE m_age;
};

int main()
{
    Person<string, int> p1 ("Yeah", 20);
    p1.showPerson();
}
3.1.2.2 类模板与函数模板区别

1、类模板无法自动推导类型,只支持显示的参数类型输入;

2、类模板在模板参数列表中可以有默认参数

3.1.2.3 类模板中成员函数创建时机

类模板中成员函数在调用时才去创建;而普通类中的成员函数构造时就创建。

3.1.2.4 类模板对象做函数参数

建议:指定传入类型

template<class NAMETYPE, class AGETYPE>
class Person
{
public:
    Person(NAMETYPE name, AGETYPE age)
    {
        this->m_name = name;
        this->m_age = age;
    }

    void showPerson()
    {
        cout << "name: " << this->m_name << " age: " << this->m_age << endl;
    }

private:
    NAMETYPE m_name;
    AGETYPE m_age;
};

void test(Person<string, int> &p) //如左所示
{
    p.showPerson();
}

int main()
{
    Person<string, int> p1 ("Yeah", 20);
    test(p1);
}
3.1.2.5 类模板与继承

如果父类是类模板,子类需要指定出父类中T的数据类型。

template<class NAMETYPE, class AGETYPE>
class Person
{
public:
    void showPerson()
    {
        cout << "name: " << this->m_name << " age: " << this->m_age << endl;
    }

    NAMETYPE m_name;
    AGETYPE m_age;
};

class Man: public Person<string, int>
{
};

void test()
{
    Man m1;
    m1.m_name = "Yeah";
    m1.m_age = 20;
    m1.showPerson();
}

int main()
{
    test();
}
3.1.2.6 类模板成员函数类外实现

类模板中成员函数类外实现时,需要加上模板参数列表。

template<class NAMETYPE, class AGETYPE>
class Person
{
public:
    Person(NAMETYPE name, AGETYPE age);

    void showPerson();

    NAMETYPE m_name;
    AGETYPE m_age;
};

template<class NAMETYPE, class AGETYPE>                        //注意加上类模板函数声明
Person<NAMETYPE, AGETYPE>::Person(NAMETYPE name, AGETYPE age)  //注意加上类模板函数作用域
{
    this->m_name = name;
    this->m_age = age;
}

template<class NAMETYPE, class AGETYPE>            //注意加上类模板函数声明
void Person<NAMETYPE, AGETYPE>::showPerson()       //注意加上类模板函数作用域
{
    cout << "name: " << this->m_name << " age: " << this->m_age << endl;
}

void test()
{
    Person <string, int> m1 ("Yeah", 20);
    m1.showPerson();
}

int main()
{
    test();
}
3.1.2.7 类模板分文件编写

将声明(.h头文件)和实现(.cpp源文件)写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称。

3.2 STL(standard template library,标准模板库)

广义上分为容器、算法、迭代器,容器和算法通过迭代器进行连接。

细分为六大组件,
1.容器:各种数据结构,如vector、list、deque、set、map
2.算法:各种常用算法,如sort、find、copy、for_each
3.迭代器:
4.仿函数:
5.适配器:
6.空间配置器:

3.2.1 容器

3.2.1.1 string 字符串
int main()
{
    //string初始化/赋值
    string str1("ab cd");

    string str2;
    str2 = "fdsf";

    cout << "str1: " << str1 << endl;
    cout << "str2: " << str2 << endl;

    //拼接:+= 或者 append
    str1 += "1234";
    str2.append("6854");

    cout << "str1: " << str1 << endl;
    cout << "str2: " << str2 << endl;

    //find:查找
    cout << "pos: " << str1.find("cd") << endl;  //如果查找不到,返回-1
    cout << "pos: " << str2.find("f") << endl;
    cout << "pos: " << str2.rfind("f") << endl;  //从右侧开始查找第一个目标字符

    //replace
    str1.replace(5,7,"4321");                    //begin_index,end_index,可能会超出end_index以插入字符长度为准
    cout << "str1: " << str1 << endl;

    //compare
    if (str1.compare(str2) == 0)                //比较两个字符串大小,相等为0,大于为1,小于-1
        cout << "same!" << endl;
    else
        cout << "diff!" << endl;

    //modify                                    //直接修改
    str2[0] = '?';
    cout << "str2: " << str2 << endl;

    //insert                                    //插入,start_index
    str2.insert(1,"!!");
    cout << "str2: " << str2 << endl;

    //erase                                     //删除,begin_index, erase_length
    str2.erase(1,2); 
    cout << "str2: " << str2 << endl;

    //substr                                    //切片,begin_index, erase_length
    cout << "substr: " << str1.substr(0,4) << endl; 

}
3.2.1.2 vector
void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it < v.end(); it ++)
        cout << *it << " ";
    cout << endl;
}

int main()
{
    //初始化赋值:1-手动赋值
    vector<int> v1;
    v1.push_back(1);    //压入尾端
    v1.push_back(6);
    v1.push_back(3);
    v1.push_back(8);
    v1.push_back(5);
    printVector(v1);
    
    //初始化赋值:2-拷贝赋值
    vector<int>v2(v1);
    printVector(v2);
    
    //初始化赋值:3-切片赋值
    vector<int>v3(v1.begin()+1, v1.end()-1);
    printVector(v3);

    cout << "vector capacity: " << v1.capacity() << endl;
    cout << "vector size: " << v1.size() << endl;
    
    //修改vector的大小
    v1.resize(10,0);     //如果扩充大小,默认后序空间用0填充,也可自行指定
    printVector(v1);
    v1.resize(4);        //如果是缩减大小,相当于切片
    printVector(v1);
    
    //尾端删除
    v1.pop_back();
    printVector(v1);
    
    //任意位置插入
    v1.insert(v1.begin(), 0);
    printVector(v1);

    //任意位置删除,也可以是区间
    v1.erase(v1.begin());
    printVector(v1);
    
    //直接清空
    v1.clear();
    printVector(v1);

    //判断是否为空
    cout << "v1 empty? : " << v1.empty() << "v2 empty? : " << v2.empty() << endl;

    cout << "first elem: " << v2.front() << endl;
    cout << "second elem: " << v2[1] << endl;
    cout << "last elem: " << v2.back() << endl;

    //交换两个vector
    v2.swap(v3);
    printVector(v2);
    printVector(v3);
    
    //可以减少内存占用消耗
    cout << "capacity: " << v2.capacity() << " size: " << v2.size() << endl;
    v2.resize(1);
    cout << "capacity: " << v2.capacity() << " size: " << v2.size() << endl;
    vector<int>(v2).swap(v2);                                                   //注意
    cout << "capacity: " << v2.capacity() << " size: " << v2.size() << endl;

    //预留空间:如果事先知道vector后序会用到的空间,可以避免动态扩展过程中的时间浪费
    vector<int>v;
    v.reserve(100000);
    
    //排序
    printVector(v2);
    sort(v1.begin(), v2.end());
    printVector(v2);
}
3.2.1.3 deque 双端数组

双端数组,可以对头端/尾端进行插入删除操作。

deque与vector区别:
1、vector对于头部的插入删除效率低(需要移动数据),数据量越大,效率越低;deque相对速度快;
2、vector访问元素速度比deque快。

具体操作过程中的差异:

1.deque没有容量capacity;

2.deque可以对头部进行操作push_front、pop_front;

3.2.1.4 stack 栈

栈容器,现金后出,不允许有遍历行为。
栈可以为空,也可以统计元素个数。

int main()
{
    stack<int> stk;
    stk.push(1);
    stk.push(2);
    stk.push(3);
    stk.push(4);

    cout << "size: " << stk.size() << endl;

    while (!stk.empty())
    {
        cout << stk.top() << endl;
        stk.pop();
    }

    cout << "size: " << stk.size() << endl;
}
3.2.1.5 queue 队列

符合先进先出FIFO,有两个出口,不允许遍历行为。
操作和stack类似:push、pop、front、back、empty、size。

3.2.1.6 list 链表

优点:可以对任意位置进行快速插入或删除元素,可以灵活利用空间
缺点:遍历速度较慢、占用的空间也较大
注意:不是连续存储空间,链表遍历只能逐个遍历(it++),不能跳着遍历/随机访问(it+=2)

交换swap
大小size、empty、resize(没有容量capacity)
插入删除push_back/push_front、pop_back/pop_front、insert、clear、erase、remove
读取front、back(不支持[]、at)
反转reverse、排序sort(lst.sort(),默认从小到大)

所有不支持随机访问迭代器的容器,不可以用标准算法;
不支持随机访问迭代器的容器,内部会提供对应的一些算法。

自定义类型中,按照特定元素进行排序:

class Person
{
public:
    Person(string name, int age, int height)
    {
        this->m_age = age;
        this->m_name = name;
        this->m_height = height;
    }
    string m_name;
    int m_age;
    int m_height;
};

void printList(const list<Person> &lst)
{
    for (list<Person>::const_iterator it = lst.begin(); it != lst.end(); it ++)
        cout << "name: " << (*it).m_name << " age: " << (*it).m_age << " height: " << (*it).m_height << endl;;
}

bool myCompare(Person &p1, Person &p2)
{
    return p1.m_age > p2.m_age;        //此处的顺序即为需要的顺序,从大到小
}

int main()
{
    list<Person> lst;
    Person p1("zhangsan", 32, 170);
    Person p2("lisi", 35, 175);
    Person p3("wangwu", 28, 178);

    lst.push_back(p1);
    lst.push_back(p2);
    lst.push_back(p3);
    printList(lst);
    lst.sort(myCompare);        //注意此函数中的写法,并且myCompare无参数输入
    printList(lst);
}
3.2.1.7 set

所有元素在插入时自动被排序,底层结构是二叉树。

set不允许容器中有重复的元素,multiset允许重复元素。

set添加元素用insert,
大小和交换:size、empty、swap(不允许使用resize)
插入删除:insert、clear、erase(类似于list中的remove)
查找:find(查找是否存在,存在返回该元素的迭代器;若不存在,返回set.end())
统计:count(统计元素的个数)

查找的使用方法如下:

int main()
{
    multiset<int> ss;

    ss.insert(1);
    ss.insert(99);
    ss.insert(9);
    ss.insert(5);
    ss.insert(9);

    printSet(ss);
    multiset<int>::iterator pos = ss.find(999);
    if (pos == ss.end())
        cout << "not find!" << endl;
    else
        cout << "find!" << endl;

}

 利用仿函数让set按照从大到小的顺序保存:

class myCompare        //仿函数
{
public:
    bool operator()(int v1, int v2)
    {
        return v1 > v2;
    }
};

void printSet(const set<int, myCompare> &ss)
{
    for (set<int, myCompare>::const_iterator it = ss.begin(); it != ss.end(); it++)
        cout << *it << " " ;
    cout << endl;
}


int main()
{
    set<int, myCompare> ss;

    ss.insert(1);
    ss.insert(99);
    ss.insert(9);
    ss.insert(5);

    printSet(ss);
    
}

利用仿函数对自定义类型的数据进行排序:

class comparePerson
{
public:
    bool operator()(const Person &p1, const Person &p2)
    {
        return p1.m_age > p2.m_age;
    }
};

void printSet(const set<Person, comparePerson> &ss)
{
    for (set<Person, comparePerson>::const_iterator it = ss.begin(); it != ss.end(); it++)
        cout << "name: " << (*it).m_name << " age: " << (*it).m_age << endl;
}


int main()
{
    Person p1("zhangsan", 25);
    Person p2("lisi", 35);
    Person p3("wangwu", 30);

    set<Person, comparePerson> ss;

    ss.insert(p1);
    ss.insert(p2);
    ss.insert(p3);
    
    printSet(ss);

}
3.2.1.8 pair

成对出现的数据,利用对组可以返回两个数据。

//方法1    
pair<string, int> p1 ("zhangsan", 20);
cout << "name: " << p1.first << " age: " << p1.second << endl;
//方法2
pair<string, int> p2 = make_pair("zhangsan", 20);
cout << "name: " << p2.first << " age: " << p2.second << endl;
3.2.1.9 map

vector,list,map是最常用的三种容器

map中所有元素都是pair,pair中第一个元素为key,第二个元素为value,会根据key自动排序。
底层用二叉树实现。

map/multimap区别是不允许/允许重复key值。


插入:insert
删除:erase(可以按照m.begin()、区间、key值删除)、clear
查找:find
统计:count

void printMap(const map<int, int> &mm)
{
    for (map<int, int>::const_iterator it = mm.begin(); it != mm.end(); it++)
    {
        cout << "first: " << (*it).first << " second: " << (*it).second << endl;
    }
}

int main()
{
    map<int, int> mm;

    mm.insert(pair<int, int>(1,10));
    mm.insert(pair<int, int>(3,55));
    mm.insert(pair<int, int>(2,28));

    printMap(mm);

}

利用仿函数,修改排序:

class myCompare
{
public:
    bool operator()(int p1, int p2)
    {
        return p1 > p2;
    }
};

void printMap(const map<int, int, myCompare> &mm)
{
    for (map<int, int, myCompare>::const_iterator it = mm.begin(); it != mm.end(); it++)
    {
        cout << "first: " << (*it).first << " second: " << (*it).second << endl;
    }
}

int main()
{
    map<int, int, myCompare> mm;

    mm.insert(pair<int, int>(1,10));
    mm.insert(pair<int, int>(3,55));
    mm.insert(pair<int, int>(2,28));

    printMap(mm);

}

3.2.2 函数对象(仿函数)

1、函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值;
2、函数对象超出普通函数的概念,函数对象可以有自己的状态;
3、函数对象可以作为参数传递。

返回bool类型的仿函数称为谓词;
如果operator()接受一个参数,就叫做一元谓词;接受两个参数,就叫做二元谓词。

内建函数对象需要包含头文件#incude <function>。

3.2.3 算法 algorithm

3.2.3.1 遍历算法 for_each

在实际开发中是最常用的遍历算法,用法如下:

void printv(int x)
{
    cout << x << " ";
}

int main()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(22);
    v.push_back(3);
    v.push_back(44);

    for_each(v.begin(), v.end(), printv); //printv不带()

}
3.2.3.2 查找算法
3.2.3.2.1 find

内置数据类型和自定义类的查找,find的返回值是迭代器:

class Person
{
public:
    Person(string name, int age)
    {
        this->m_name = name;
        this->m_age = age;
    }

    bool operator==(const Person &p)
    {
        if (this->m_name == p.m_name && this->m_age == p.m_age)
            return true;
        else
            return false;
    }

    string m_name;
    int m_age;
};

int main()
{
    vector<int> v;
    v.push_back(1);
    v.push_back(22);
    v.push_back(3);
    v.push_back(44);

    vector<int>::iterator it = find(v.begin(), v.end(), 23);
    if (it == v.end())
        cout << "not find!" << endl;
    else
        cout << "find!" << endl;

    vector<Person> vp;
    Person p1("aaa", 24);
    Person p2("bbb", 63);
    Person p3("ccc", 34);
    Person p4("ddd", 35);
    vp.push_back(p1);
    vp.push_back(p2);
    vp.push_back(p3);

    vector<Person>::iterator it2 = find(vp.begin(), vp.end(), p4);
    if (it2 == vp.end())
        cout << "not find!" << endl;
    else
        cout << "find!" << endl;
}

二分法查找数据,要求为排序数据,返回的是bool类型数据

int main()
{
    set<int> v;
    v.insert(1);
    v.insert(22);
    v.insert(3);
    v.insert(44);

    bool ret = binary_search(v.begin(), v.end(), 22);
    cout << ret << endl;

}
3.2.3.2.3 count

统计数据出现次数,返回整形int。

3.2.3.3 排序算法
3.2.3.3.1 sort

默认从小到大,如果要从大到小需要加谓词。

sort(v.begin(), v.end());
sort(v.begin(), v.end(), greater<int>());
3.2.3.3.2 random_shuffle 随机打乱

使用时记得加随机种子

rand_shuffle(v.begin(), v.end());
3.2.3.3.3 merge 合并后排序
int main()
{
    set<int> v1;                            //合并的两个数组需要为排序数组
    set<int> v2;                            //合并的两个数组需要为排序数组
    for (int i = 0; i<5; i++)
    {
        v1.insert(i);
        v2.insert(i+1);
    }

    vector<int> vTarget;                    //定义目标接受的vector
    vTarget.resize(v1.size()+v2.size());    //记得需要预先分配空间

    merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
}
3.2.3.3.4 reverse 反转
reverse(v.begin(), v.end());
3.2.3.4 拷贝和替换
3.2.3.4.1 copy
copy(v.begin(), v.end(), vTarget.begin());
3.2.3.4.2 replace
replace(v.begin(), v.end(), oldvalue, newvalue);
3.2.3.4.3 swap

同种类型容器内容互换:

swap(container c1, container c2);
3.2.3.5 算术生成算法

头文件为<numeric>

3.2.3.5.1 accumulate 累加
accumulate(v.begin(), v.end(), init_sum_value);
3.2.3.5.2 fill 后期重新填充
vector<int>v;
v.resize(10);

fill(v.begin(), v.end(), 100);
3.2.3.6 集合算法

要求原有的两个容器内都是按序排列数据

3.2.3.6.1 set_intersection 交集

目标容器开辟空间需要从两个容器中取较小值,set_intersection返回值是交集中最后一个元素的位置。

set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTraget.begin());
3.2.3.6.2 set_union 并集

目标容器开辟空间需要取两个容器大小的和,set_union返回值是交集中最后一个元素的位置。

set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vTraget.begin());
3.2.3.6.3 set_difference 差集

目标容器开辟空间需要从两个容器中取较大值(实际上可以取被比较容量的大小),set_difference返回值是交集中最后一个元素的位置。

set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTraget.begin());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值