-
c++限制小数的位数
//主要是对cin,cout之类的一些操纵运算子,比如setfill,setw,setbase,setprecision等等。 //它是I/O流控制头文件,就像C里面的格式化输出一样.以下是一些常见的控制函数的:dec 置基数为10 相当 于"%d"hex 置基数为16 相当于"%X"oct 置基数为8 相当于"%o"setfill© 设填充字符为csetprecision(n) 设显 //示小数精度为n位setw(n) 设域宽为n个字符这个控制符的意思是保证输出宽度为n。如: cout<<setw(3)<<1<<setw(3)<<10<<setw(3)<<100; 输出结果为1 10100 (默认是右对齐)当输出长度大于3时(<<1000) setw(3)不起作用。setioflags(ios::fixed) //固定的浮点显示… 例如: const double value = 12.3456789; cout << value << endl; // 默认以6精度,所以输出为 12.3457 cout << setprecision(4) << value << endl; // 改成4精度,所以输出为12.35 cout << setprecision(8) << value << endl; // 改成8精度,所以输出为12.345679 cout << fixed << setprecision(4) << value << endl; // //加了fixed意味着是固定点方式显示,所以这里的精度指的是小数位,输出为12.3457 cout << value << endl; //
-
=初始化类调用的是拷贝构造函数
c++中
vector详解
1. 头文件
2. vector声明及初始化
vector<int> vec; //声明一个int型向量
vector<int> vec(5); //声明一个初始大小为5的int向量
vector<int> vec(10, 1); //声明一个初始大小为10且值都是1的向量
vector<int> vec(tmp); //声明并用tmp向量初始化vec向量
vector<int> tmp(vec.begin(), vec.begin() + 3); //用向量vec的第0个到第2个值初始化tmp
int arr[5] = {1, 2, 3, 4, 5};
vector<int> vec(arr, arr + 5); //将arr数组的元素用于初始化vec向量
//说明:当然不包括arr[4]元素,末尾指针都是指结束元素的下一个元素,
//这个主要是为了和vec.end()指针统一。
vector<int> vec(&arr[1], &arr[4]); //将arr[1]~arr[4]范围内的元素作为vec的初始值
3. vector基本操作
(1). 容量
-
向量大小: vec.size();
-
向量最大容量: vec.max_size();
-
更改向量大小: vec.resize();
-
向量真实大小: vec.capacity();
-
向量判空: vec.empty();
-
减少向量大小到满足元素所占存储空间的大小: vec.shrink_to_fit(); //
(2). 修改
-
多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值
-
末尾添加元素: vec.push_back();
-
末尾删除元素: vec.pop_back();
-
任意位置插入元素: vec.insert();
-
任意位置删除元素: vec.erase();
-
交换两个向量的元素: vec.swap();
-
清空向量元素: vec.clear();
(3)迭代器
-
开始指针:vec.begin();
-
末尾指针:vec.end(); //指向最后一个元素的下一个位置
-
指向常量的开始指针: vec.cbegin(); //意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。
-
指向常量的末尾指针: vec.cend();
(4)元素的访问
-
下标访问: vec[1]; //并不会检查是否越界
-
at方法访问: vec.at(1); //以上两者的区别就是at会检查是否越界,是则抛出out of range异常
-
访问第一个元素: vec.front();
-
访问最后一个元素: vec.back();
-
返回一个指针: int* p = vec.data(); //可行的原因在于vector在内存中就是一个连续存储的数组,所以可以返回一个指针指向这个数组。这是是C++11的特性。
4. 算法
-
遍历元素
vector<int>::iterator it;
for (it = vec.begin(); it != vec.end(); it++)
cout << *it << endl;
//或者
for (size_t i = 0; i < vec.size(); i++) {
cout << vec.at(i) << endl;
}
-
元素翻转
reverse(vec.begin(), vec.end());
-
元素排序
#include <algorithm>
sort(vec.begin(), vec.end()); //采用的是从小到大的排序
//如果想从大到小排序,可以采用上面反转函数,也可以采用下面方法:
bool Comp(const int& a, const int& b) {
return a > b;
}
sort(vec.begin(), vec.end(), Comp);
c++中map详解
map简介
map是STL的一个关联容器,以键值对存储的数据,其类型可以自己定义,每个关键字在map中只能出现一次,关键字不能修改,值可以修改;map同set、multiset、multimap(与map的差别仅在于multimap允许一个键对应多个值)内部数据结构都是红黑树,而java中的hashmap是以hash table实现的。所以map内部有序(自动排序,单词时按照字母序排序),查找时间复杂度为O(logn)。
map用法
1、头文件
2、定义
map<string,int> my_map;
也可以使用
typedef map<string,int> My_Map;
My_Map my_map;
3、基本方法
4、map插入数据的几种方法
//第一种:用insert函数插入pair数据:
map<int,string> my_map;
my_map.insert(pair<int,string>(1,"first"));
my_map.insert(pair<int,string>(2,"second"));
//第二种:用insert函数插入value_type数据:
map<int,string> my_map;
my_map.insert(map<int,string>::value_type(1,"first"));
my_map.insert(map<int,string>::value_type(2,"second"));
map<int,string>::iterator it; //迭代器遍历
for(it=my_map.begin();it!=my_map.end();it++)
cout<<it->first<<it->second<<endl;
//第三种:用数组的方式直接赋值:
map<int,string> my_map;
my_map[1]="first";
my_map[2]="second";
map<int,string>::iterator it;
for(it=my_map.begin();it!=my_map.end();it++)
cout<<it->first<<it->second<<endl;
5、查找元素(判定这个关键字是否在map中出现)
第一种:用count函数来判断关键字是否出现,其缺点是无法定位元素出现的位置。由于map一对一的映射关系,count函数的返回值要么是0,要么是1。
map<string,int> my_map;
my_map["first"]=1;
cout<<my_map.count("first")<<endl; //输出1;
第二种:用find函数来定位元素出现的位置,它返回一个迭代器,当数据出现时,返回的是数据所在位置的迭代器;若map中没有要查找的数据,返回的迭代器等于end函数返回的迭代器。
using namespace std;
int main()
{
map<int, string> my_map;
my_map.insert(pair<int, string>(1, "student_one"));
my_map.insert(pair<int, string>(2, "student_two"));
my_map.insert(pair<int, string>(3, "student_three"));
map<int, string>::iterator it;
it = my_map.find(1);
if(it != my_map.end())
cout<<"Find, the value is "<<it->second<<endl;
else
cout<<"Do not Find"<<endl;
return 0;
}
//通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据iterator->first和iterator->second,分别代表关键字和value值。
6、删除元素
using namespace std;
int main()
{
map<int, string> my_map;
my_map.insert(pair<int, string>(1, "one"));
my_map.insert(pair<int, string>(2, "two"));
my_map.insert(pair<int, string>(3, "three"));
//如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好
//如果要删除1,用迭代器删除
map<int, string>::iterator it;
it = my_map.find(1);
my_map.erase(it); //如果要删除1,用关键字删除
int n = my_map.erase(1); //如果删除了会返回1,否则返回0
//用迭代器,成片的删除
//一下代码把整个map清空
my_map.erase( my_map.begin(), my_map.end() );
//成片删除要注意的是,也是STL的特性,删除区间是一个前闭后开的集合
//自个加上遍历代码,打印输出吧
return 0;
}
7、排序,按value排序
map中元素是自动按key升序排序(从小到大)的;按照value排序时,想直接使用sort函数是做不到的,sort函数只支持数组、vector、list、queue等的排序,无法对map排序,那么就需要把map放在vector中,再对vector进行排序。
using namespace std;
bool cmp(pair<string,int> a, pair<string,int> b) {
return a.second < b.second;
}
int main()
{
map<string, int> ma;
ma["Alice"] = 86;
ma["Bob"] = 78;
ma["Zip"] = 92;
ma["Stdevn"] = 88;
vector< pair<string,int> > vec(ma.begin(),ma.end());
//或者:
//vector< pair<string,int> > vec;
//for(map<string,int>::iterator it = ma.begin(); it != ma.end(); it++)
// vec.push_back( pair<string,int>(it->first,it->second) );
sort(vec.begin(),vec.end(),cmp);
for (vector< pair<string,int> >::iterator it = vec.begin(); it != vec.end(); ++it)
{
cout << it->first << " " << it->second << endl;
}
return 0;
}
函数的调用可以作为左值
如果函数的返回值是引用,这个函数调用可以作为左值。
using spacename std;
int &test()
{
static int a = 10;
return a;
}
int main()
{
int &ref = test();
cout << ref << endl; //10
cout << ref << endl; //10
test() = 1000;
cout << ref << endl; //1000
cout << ref << endl; //1000
return 0;
}
引用的本质
引用必须引用一块合法的内存空间
结论:C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
函数提高
函数默认参数
注意:
-
如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值
-
如果函数声明有默认参数,函数实现就不能有默认参数,如果函数实现有默认参数,函数声明就不能有默认参数,即声明和实现只能有一个有默认参数。
函数的占位参数
函数重载
可以让函数名称相同,提高复用性
需满足条件:
-
同一个作用域下
-
函数名称相同
-
函数参数类型不同,或者个数不同,或者顺序不同
注意函数的返回值不能作为函数重载的条件
类和对象
C++面向对象的三大特性为:封装、继承、多态
封装的权限控制
struct和class的区别
struct 默认权限是 共有 public class 默认权限是 私有 private
成员属性设置为私有
-
可以自己控制读写权限
-
对于写可以检测数据的有效性
运算符重载
+-运算符重载
using namespace std;
class Person
{
public:
////成员函数重载
//Person operator+(Person& p)
//{
// Person temp;
// temp.m_a = this->m_a + p.m_a;
// temp.m_b = this->m_b + p.m_b;
// return temp;
//}
int m_a;
int m_b;
};
//全局函数重载
Person operator+(Person& p1, Person& p2)
{
Person temp;
temp.m_a = p1.m_a + p2.m_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
//函数重载的版本
Person operator+(Person& p1, int num)
{
Person temp;
temp.m_a = p1.m_a + num;
temp.m_b = p1.m_b + num;
return temp;
}
void test01()
{
Person p1;
p1.m_a = 10;
p1.m_b = 10;
Person p2;
p2.m_a = 10;
p2.m_b = 10;
//成员函数的本质调用
//Person p3 = p1.operator+(p2);
//全局函数的本质调用
//Person p3 = operator(p1, p2);
Person p3 = p1 + p2;
Person p4 = p1 + 100;
cout << "p3.m_a = " << p3.m_a << endl;
cout << "p3.m_b = " << p3.m_b << endl;
cout << "p4.m_a = " << p4.m_a << endl;
cout << "p4.m_b = " << p4.m_b << endl;
}
int main()
{
test01();
}
递增运算符重载
using namespace std;
class Myint
{
public:
friend ostream& operator<<(ostream& cout, Myint myint);
Myint(int a):m_num(a) {}
//重载前置++运算符
Myint &operator++()
{
//先进行递增操作
m_num++;
//在将
return *this;
}
//重载后置++运算符
Myint operator++(int) //int代表占位参数,可以用于区分前置和后置递增
{
//先 记录当前数值
Myint temp = *this;
//后 递增
m_num++;
//最后将记录结果做返回
return temp;
}
private:
int m_num;
};
//重载左移运算符
ostream& operator<<(ostream& cout, Myint myint)
{
cout << myint.m_num;
return cout;
}
//void test01()
//{
// Myint myint(10);
// cout << ++myint << endl;
//}
void test02()
{
Myint myint(10);
cout << myint++ << endl;
cout << myint << endl;
}
int main()
{
//test01();
test02();
}
等号运算符重载(默认等号为浅拷贝)
using namespace std;
class Person
{
public:
Person(int age)
{
m_age = new int(age);
}
~Person()
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
}
//重载等号运算符
Person & operator=(Person &p)
{
//编译器提供浅拷贝
//m_age = p.m_age
//应该先判断是否有属性在堆区,如果有,先释放干净,如何在深拷贝
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
//深拷贝
m_age = new int(*p.m_age);
//返回对象本身
return *this;
}
int* m_age;
};
void test01()
{
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1;//赋值操作
cout << "p1的年龄为为: " << *p1.m_age << endl;
cout << "p2的年龄为为: "<< *p2.m_age << endl;
cout << "p3的年龄为为: " << *p3.m_age << endl;
}
int main()
{
test01();
//int a = 10;
//int b = 20;
//int c = 30;
//c = b = a;
//cout << "a=" << a << endl;
//cout << "b=" << b << endl;
//cout << "c=" << c << endl;
}
关系运算符重载
using namespace std;
class Person
{
public:
Person(string name,int age):m_name(name),m_age(age){}
bool operator==(Person& p)
{
if (this->m_name == p.m_name && this->m_age == p.m_age)
{
return true;
}
return false;
}
bool operator!=(Person& p)
{
if (this->m_name == p.m_name && this->m_age == p.m_age)
{
return false;
}
return true;
}
string m_name;
int m_age;
};
void test01()
{
Person p1("Tom", 18);
Person p2("Tom", 18);
if (p1 == p2)
{
cout << "p1 p2 是相等的!" << endl;
}
else
{
cout << "p1 p2是不相等的!" << endl;
}
if (p1 != p2)
{
cout << "p1 p2是不相等的!" << endl;
}
else
{
cout << "p1 p2 是相等的!" << endl;
}
}
int main()
{
test01();
return 0;
}
函数调用运算符重载(仿函数)
using namespace std;
//函数调用运算符重载
class Myprint
{
public:
void operator()(string test)
{
cout << test << endl;
}
};
void Myprint02(string test)
{
cout << test << endl;
}
void test01()
{
Myprint myprint;
myprint("Hello world!!!");//由于使用起来非常类似与函数调用,因此称为仿函数
Myprint02("Hello world!!!");
}
//仿函数非常灵活,没有固定写法
//加法类
class Myadd
{
public:
int operator()(int num1, int num2)
{
return num1 + num2;
}
};
void test02()
{
Myadd myadd;
int ret = myadd(100, 100);
cout << ret << endl;
//匿名函数对象
cout << Myadd()(100, 100) << endl;
}
int main()
{
//test01();
test02();
}
左移运算符重载
using namespace std;
class Person
{
public:
int m_a;
int m_b;
};
ostream &operator<<(ostream& cout, Person& p)
{
cout << "m_a = " << p.m_a << " m_b = " << p.m_b ;
return cout;
}
void test01()
{
Person p;
p.m_a = 10;
p.m_b = 10;
cout << p << endl;;
}
int main()
{
test01();
}
继承
继承的基本语法
using namespace std;
// 普通实现页面
// Jave页面
class Java
{
public:
void header()
{
cout << "首页 公开课 登录 注册 " << endl;
}
void footer()
{
cout << "帮助中心 合作交流 站内地图" << endl;
}
void left()
{
cout << "Jave Pyton C++ " << endl;
}
void content()
{
cout << "Jave 学科视频" << endl;
}
};
class Python
{
public:
void header()
{
cout << "首页 公开课 登录 注册 " << endl;
}
void footer()
{
cout << "帮助中心 合作交流 站内地图" << endl;
}
void left()
{
cout << "Jave Pyton C++ " << endl;
}
void content()
{
cout << "Python 学科视频" << endl;
}
};
class Cpp
{
public:
void header()
{
cout << "首页 公开课 登录 注册 " << endl;
}
void footer()
{
cout << "帮助中心 合作交流 站内地图" << endl;
}
void left()
{
cout << "Jave Pyton C++ " << endl;
}
void content()
{
cout << "C++ 学科视频" << endl;
}
};
void test01()
{
cout << "Jave 下载视频页面如下" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "--------------------------------" << endl;
cout << "Python 下载视频页面如下" << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << "--------------------------------" << endl;
cout << "C++ 下载视频页面如下" << endl;
Cpp cp;
cp.header();
cp.footer();
cp.left();
cp.content();
}
int main()
{
test01();
return 0;
}
继承实现
#include<iostream>
using namespace std;
// 继承实现页面
class BasePage
{
public:
void header()
{
cout << "首页 公开课 登录 注册 " << endl;
}
void footer()
{
cout << "帮助中心 合作交流 站内地图" << endl;
}
void left()
{
cout << "Jave Pyton C++ " << endl;
}
};
// Jave页面
class Java:public BasePage
{
public:
void content()
{
cout << "Jave 学科视频" << endl;
}
};
class Python :public BasePage
{
public:
void content()
{
cout << "Python 学科视频" << endl;
}
};
class Cpp :public BasePage
{
public:
void content()
{
cout << "C++ 学科视频" << endl;
}
};
void test01()
{
cout << "Jave 下载视频页面如下" << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "--------------------------------" << endl;
cout << "Python 下载视频页面如下" << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << "--------------------------------" << endl;
cout << "C++ 下载视频页面如下" << endl;
Cpp cp;
cp.header();
cp.footer();
cp.left();
cp.content();
}
int main()
{
test01();
return 0;
}
继承方式
公共 私有 保护
继承中的对象模型
继承中构造和析构顺序
先构造父类,在构造子类,析构顺序相反
继承同名成员处理方式
问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
-
访问子类同名成员直接访问即可
-
访问父类同名成员需要加作用域
-
若子类中出现了和父类同名的成员函数,子类的同名函数会隐藏掉父类中所有同名函数,加作用域才可以访问到
继承中同名静态函数成员处理方式
问题:继承中同名的静态成员在子类对象上如何进行访问?
-
静态成员和非静态成员出现同名,处理方式一致
-
访问子类同名成员直接访问即可
-
访问父类同名成员需要加作用域
多继承语法
C++允许一个类继承多个类
语法: class子类:继承方式 父类1, 继承方式 父类2...
多继承可能会引发父类中有同名成员出现,需要加作用域区分 ,C++实际开发中不建议用多继承
总结: 多继承中如果父类中出现了同名情况,子类使用时候要加作用域
菱形继承
菱形继承问题:
-
羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
-
草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
#include<iostream>
using namespace std;
//动物类
class Animal
{
public:
int age;
};
//利用虚继承,解决菱形继承的问题
//继承之前加关键字virtual 变为虚继承
//Animal类 称为虚基类
//羊类
class Sheep :virtual public Animal
{
};
//骆驼类
class Tuo :virtual public Animal
{
};
//羊驼类
class SheepTuo : public Sheep, public Tuo
{
};
void test01()
{
SheepTuo St;
St.Sheep::age = 18;
St.Tuo::age = 38;
//菱形继承两个父类拥有相同属性,需加作用域加以区分
cout << "St.Sheep::age = " << St.Sheep::age << endl;
cout << "St.Tuo::age = " << St.Tuo::age << endl;
cout << "St.age = " << St.age << endl;
//这份数据我们知道只有一份就可以,菱形继承导致数女接有两份,资源浪费
}
int main()
{
test01();
// system("pause");
return 0;
}
总结:
-
菱形继承带来的主要问题是好类继承两份相同的数据,导致资源浪费以及毫无意义
-
利用虚继承可以解决菱形继承问题,继承之前加关键字virtual 变为虚继承
多态
多态的基本概念
多态是C++面向对象三大特性之一
多态分为两类
-
静态多态:函数重载和运算符重载属于静态多态,复用函数格
-
动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别:
-
静态多态的函数地址早绑定 - 编译阶段确定函数地址
-
动态多态的函数地址晚绑定 - 运行阶段确定函数地址
动态多态满足条件
-
有继承关系
-
子类重写父类的虚函数
重写: 函数返回值类型 函数名 参数列表完全一致称为重写
动态多态使用:父类的指针或者引用,指向子类对象
#include<iostream>
using namespace std;
class Animal
{
public:
//虚函数
virtual void speak()
{
cout << "动物在说话" << endl;
}
};
class Cat : public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};
class Dog : public Animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
//执行说话的函数
//地址早绑定 在编译阶段确定函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定
void dospeak(Animal &animal) //Animal &animal =cat
{
animal.speak();
}
void test01()
{
Cat cat;
dospeak(cat);
Dog dog;
dospeak(dog);
}
int main()
{
test01();
// system("pause");
return 0;
}
未重写时
重写后
多态案例-----计算器类
案例描述: 分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类
多态的优点:
-
代码组织结构清晰
-
可读性强
-
利于前期和后期的扩展以及维护
#include<iostream>
#include<string.h>
using namespace std;
//普通写法
class Calculator
{
public:
int getResult(string oper)
{
if (oper == "+")
{
return a + b;
}
else if (oper == "-")
{
return a - b;
}
else if (oper == "*")
{
return a * b;
}
}
int a;
int b;
};
void test01()
{
Calculator c;
c.a = 10;
c.b = 30;
cout << c.a << "+" << c.b << "=" << c.getResult("+") << endl;
cout << c.a << "-" << c.b << "=" << c.getResult("-") << endl;
cout << c.a << "*" << c.b << "=" << c.getResult("*") << endl;
}
//利用多态实现
//实现计算器抽象类
class AbstractCalculator
{
public:
virtual int getResult()
{
return 0;
}
int a;
int b;
};
class AddCalculator :public AbstractCalculator
{
public:
int getResult()
{
return a + b;
}
};
class SubCalculator :public AbstractCalculator
{
public:
int getResult()
{
return a - b;
}
};
class MulCalculator :public AbstractCalculator
{
public:
int getResult()
{
return a * b;
}
};
void test02()
{
//多态使用条件
//父类指针或者引用指向子类对象
AbstractCalculator* abc = new AddCalculator();
abc->a = 100;
abc->b = 20;
cout << abc->a << "+" << abc->b << "=" << abc->getResult()<< endl;
delete abc;
abc = new SubCalculator();
abc->a = 100;
abc->b = 20;
cout << abc->a << "-" << abc->b << "=" << abc->getResult() << endl;
delete abc;
abc = new MulCalculator();
abc->a = 100;
abc->b = 20;
cout << abc->a << "*" << abc->b << "=" << abc->getResult() << endl;
delete abc;
}
int main()
{
//test01();
test02();
// system("pause");
return 0;
}
多态好处:
-
组织结构清晰
-
可读性强
-
对于前期和后期扩展以及维护性高
纯虚函数和抽象类
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是是调用子类重写的内容,因此可以将虚函数改为纯虚函数
纯虚函数语法: virtual 返回值类型 函数名 (参数列表) = 0 当类中有了纯虚函数,这个类也称为抽象类。只要有了一个纯虚函数就叫抽象类
抽象类特点:
-
无法实例化对象
-
子类必须重写抽象类中的纯虚函数,否则也属于抽象类
#include<iostream>
using namespace std;
class Base
{
public:
// 只要有一个纯虚函数,这个类被称为抽象类
virtual void func() = 0; // 纯虚函数
};
class Son :public Base
{
public:
virtual void func(){
cout << "func 函数调用" << endl;
};
};
void test01()
{
//Base b; // 抽象类无法实例化对象
//new Base; // 抽象类无法实例化对象
Base* base = new Son;
base->func();
delete base;
}
int main()
{
test01();
return 0;
}
多态案例2———制作饮品
#include<iostream>
using namespace std;
class AbstractDrinking
{
public:
// 煮水
virtual void Boil() = 0;
// 冲泡
virtual void Brew() = 0;
// 倒入杯中
virtual void PourInCup() = 0;
// 加入佐料
virtual void PutSomething() = 0;
// 制作饮品
void makeDrink()
{
Boil();
Brew();
PourInCup();
PutSomething();
}
};
class Coffee : public AbstractDrinking
{
public:
// 煮水
virtual void Boil() {
cout << "Coffee 1" << endl;
}
// 冲泡
virtual void Brew() {
cout << "Coffee 2" << endl;
}
// 倒入杯中
virtual void PourInCup() {
cout << "Coffee 3" << endl;
}
// 加入佐料
virtual void PutSomething() {
cout << "Coffee 4" << endl;
}
};
class Tea : public AbstractDrinking
{
public:
// 煮水
virtual void Boil() {
cout << "Tea 1" << endl;
}
// 冲泡
virtual void Brew() {
cout << "Tea 2" << endl;
}
// 倒入杯中
virtual void PourInCup() {
cout << "Tea 3" << endl;
}
// 加入佐料
virtual void PutSomething() {
cout << "Tea 4" << endl;
}
};
void dowork(AbstractDrinking* abs)
{
abs->makeDrink();
delete abs;
}
void test01()
{
dowork(new Coffee);
cout << "--------------------" << endl;
dowork(new Tea);
}
int main()
{
test01();
return 0;
}
虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
-
可以解决父类指针释放子类对象
-
都需要有具体的函数实现
虚析构和纯虚析构区别:
-
如果是纯虚析构,该类属于抽象类,无法实例化对象
总结:
-
虚析构或纯虚析构就是用来解决通过父类指针释放子类对像
-
如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
-
拥有纯虚析构函数的类也属于抽象类
#include<iostream>
#include<string.h>
using namespace std;
class Animal
{
public:
Animal() {
cout << "Animal create " << endl;
}
// 利用虚析构可以解决 父类指针释放子类对象时不干净的问题
//virtual ~Animal() {
// cout << "Animal destory" << endl;
//}
// 纯虚析构 需要声明也需要实现
// 有了纯虚析构,这个类也属于抽象类,无法实例化对象
virtual ~Animal() = 0;
virtual void speak() = 0;
};
Animal::~Animal()
{
cout << "Animal purity virtual destory" << endl;
}
class Cat : public Animal
{
public:
Cat(string name) :cname(new string(name)){
cout << "Cat create " << endl;
}
virtual void speak() {
cout << *cname <<"小猫在说话" << endl;
}
~Cat() {
if (cname != NULL) {
cout << "Cat destory" << endl;
delete cname;
cname = NULL;
}
}
string* cname;
};
void test01()
{
Animal* animal = new Cat("SuYou");
animal->speak();
// 父类指针在析构时不会调用子类中析构函数,导致子类中若有堆区数据,出现内存泄露
delete animal;
}
int main()
{
test01();
return 0;
}
多态案例3——电脑组装
#include<iostream>
using namespace std;
// 抽象不同零件类
// 抽象CPU类
class CPU
{
public:
virtual void calculate() = 0;
};
class VideoCard
{
public:
virtual void display() = 0;
};
class Memory
{
public:
virtual void storage() = 0;
};
// 电脑类
class Computer
{
public:
Computer(CPU * cpu,VideoCard *vc,Memory *mem):m_cpu(cpu),m_vc(vc),m_mem(mem) {}
void work()
{
m_cpu->calculate();
m_vc->display();
m_mem->storage();
}
~Computer() {
if (m_cpu != NULL) {
delete m_cpu;
m_cpu = NULL;
}
if (m_vc != NULL) {
delete m_cpu;
m_vc = NULL;
}
if (m_mem != NULL) {
delete m_cpu;
m_mem = NULL;
}
}
private:
CPU* m_cpu; // CPU的零件指针
VideoCard* m_vc; // 显卡的零件指针
Memory* m_mem; // 内存条的零件指针
};
//具体厂商
//Intel厂商
class IntelCPU:public CPU
{
public:
virtual void calculate() {
cout << "Intel CPU start calculate" << endl;;
}
};
class InteIViedoCard :public VideoCard
{
public:
virtual void display() {
cout << "Intel VideoCard start dispaly" << endl;;
}
};
class IntelMemory :public Memory
{
public:
virtual void storage() {
cout << "Intel Memory start memory" << endl;;
}
};
// Lenovo厂商
class LenovoCPU :public CPU
{
public:
virtual void calculate() {
cout << "Lenovo CPU start calculate" << endl;;
}
};
class LenovoViedoCard :public VideoCard
{
public:
virtual void display() {
cout << "Lenovo VideoCard start dispaly" << endl;;
}
};
class LenovoMemory :public Memory
{
public:
virtual void storage() {
cout << "Lenovo Memory start memory" << endl;;
}
};
void test01()
{
cout << "第一台电脑--------------" << endl;
// 第一台电脑零件
CPU* intelCpu = new IntelCPU;
VideoCard* intelCard = new InteIViedoCard;
Memory* intelMem = new IntelMemory;
// 创建第一台电脑
Computer* computer1 = new Computer(intelCpu, intelCard, intelMem);
computer1->work();
delete computer1;
cout << "第二台电脑--------------" << endl;
// 创建第二台电脑
Computer* computer2 = new Computer(new LenovoCPU,new LenovoViedoCard,new LenovoMemory);
computer2->work();
delete computer2;
cout << "第三台电脑--------------" << endl;
// 创建第三台电脑
Computer* computer3 = new Computer(new LenovoCPU, new InteIViedoCard, new LenovoMemory);
computer3->work();
delete computer3;
}
int main()
{
test01();
return 0;
}
文件操作
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放 通过文件可以将数据持久化 C++中对文件操作需要包含头文件<fstream>
文件类型分为两种:
-
文本文件——文件以文本的ASCII码形式存储在计算机中
-
二进制文件——文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
操作文件的三大类:
-
ofstream:写操作
-
ifstream:读操作
-
fstream:读写操作
文本文件
写文件
写文件步骤如下:
-
包含头文件 #include <fstream>
-
创建流对象 ofstream ofs;
-
打开文件 ofs.open("文件路径",打开方式);
-
写数据
ofs<<"写入的数据";
-
关闭文件
ofs.close();
文件打开方式:
注意:文件打开方式可以配合使用,利用|操作符 例如:用二进制方式写文件 ios::binary | ios:: out
#include<iostream>
using namespace std;
#include<fstream>
void test01() {
ofstream ofs;
ofs.open("W:\\代码\\c++学习\\黑马\\文件操作学习\\test.txt", ios::out);
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:18" << endl;
ofs.close();
}
int main()
{
test01();
return 0;
}
总结:
-
文件操作必须包含头文件fstream
-
读文件可以利用ofstream,或者fstream类
-
打开文件时候需要指定操作文件的路径,以及打开方式
-
利用<<可以向文件中写数据
-
操作完毕,要关闭文件
读文件
读文件与写文件步骤相似,但是读取方式相对于比较多
读文件步骤如下:
-
包含头文件
#include <fstream>
-
创建流对象
ifstream ifs;
-
打开文件并判断文件是否打开成功
ifs.open("文件路径",打开方式);
-
读数据
四种方式读取
-
关闭文件
ifs.close();
#include<iostream>
using namespace std;
#include<fstream>
#include<string>
void test01() {
ifstream ifs;
ifs.open("W:\\代码\\c++学习\\黑马\\文件操作学习\\test.txt", ios::in);
if (!ifs.is_open())
{
cout << "打开文件失败"<< endl;
return;
}
//第一种
//char buf[1024] = { 0 };
//while (ifs >> buf) {
// cout << buf << endl;
//}
//ifs.close();
//第二种
//char buf[1024] = { 0 };
//while (ifs.getline(buf,sizeof(buf))) {
// cout << buf << endl;
//}
//第三种
//string buf;
//while ( getline(ifs,buf)) {
// cout << buf << endl;
//}
//第四种,不常用
//char c;
//while ((c = ifs.get()) != EOF)
//{
// cout << c;
//}
ifs.close();
}
int main()
{
test01();
return 0;
}
总结:
-
读文件可以利用ifstream,或者fstream类
-
利用is_open函数可以判断文件是否打开成功close关闭文件
二进制文件
以二进制的方式对文件进行读写操作 打开方式要指定为ios::binary
写文件
二进制方式写文件主要利用流对象调用成员函数write 函数原型: ostream& write(const char * buffer,int len); 参数解释: 字符指针buffer指向内存中一段存储空间。len是读写的字节数
#include<iostream>
using namespace std;
#include<fstream>
class Person
{
public:
char m_Name[64];
int m_Age;
};
void test01() {
ofstream ofs;
ofs.open("W:\\代码\\c++学习\\黑马\\文件操作学习\\Person.txt", ios::binary | ios::out);
Person p = { "张三",18 };
ofs.write((const char*)&p, sizeof(Person));
ofs.close();
}
int main()
{
test01();
return 0;
}
总结: 文件输出流对象可以通过write函数,以二进制方式写数据
读文件
二进制方式读文件主要利用流对象调用成员函数read 函数原型: istream&read(char *buffer,int len); 参数解释: 字符指针buffer指向内存中一段存储空间。len是读写的字节数
#include<iostream>
using namespace std;
#include<fstream>
class Person
{
public:
char m_Name[64];
int m_Age;
};
void test01() {
ifstream ifs;
ifs.open("W:\\代码\\c++学习\\黑马\\文件操作学习\\Person.txt", ios::binary | ios::in);
if (!ifs.is_open())
{
cout << "打开文件失败"<< endl;
return;
}
Person p;
ifs.read((char*)&p, sizeof(Person));
cout << p.m_Name << p.m_Age << endl;
ifs.close();
}
int main()
{
test01();
return 0;
}
模板
C++提供两种模板机制:函数模板和类模板
模板的特点:
-
模板不可以直接使用,它只是一个框架
-
模板的通用并不是万能的
函数模板
函数模板作用:
-
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
语法:
template<typename T>
函数声明或定义
解释:
-
template--声明创建模板
-
typename--.表面其后面的符号是一种数据类型,可以用class代替
-
T--通用的数据类型,名称可以替换,通常为大写字母
#include<iostream>
using namespace std;
void swapint(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
void swapdouble(double& a, double& b)
{
double temp = a;
a = b;
b = temp;
}
template<typename T>
void myswap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
void test01() {
int a = 10, b = 20;
//swapint(a, b);
//cout << a << b << endl;
double c = 1.11, d = 2.22;
//swapdouble(c, d);
//cout << c << d << endl;
//两种方法使用函数模板
//1.自动类型推导
//myswap(a, b);
//cout << a << b << endl;
//2.显示指定类型
myswap<int>(a, b);
cout << a << b << endl;
}
int main()
{
test01();
return 0;
}
总结:
-
函数模板利用关键字template
-
使用函数模板有两种方式:自动类型推导、显示指定类型
-
模板的目的是为了提高复用性,将类型参数化
注意事项
-
自动类型推导,必须推导出一致的数据类型T,才可以使用
-
模板必须要确定出T的数据类型,才可以使用
案例
案例描述:
-
利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
-
排序规则从大到小,排序算法为选择排序
-
分别利用char数组和int数组进行测试
#include<iostream>
using namespace std;
//实现通用对数组进行排序的函数
//规则从大到小
//算法选择
//测试 char数组、int数组
template<typename T>
void myswap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template<typename T>
void mysort(T arr[],int len)
{
for (int i = 0; i < len; i++)
{
int max = i;//认定最大值下标
for (int j = i + 1; j < len; j++)
{
if (arr[max] < arr[j])
{
max = j;//更新最大值下标
}
}
if (max != i)
{
myswap(arr[max], arr[i]);
}
}
}
template<typename T>
void printArry(T arr[], int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i];
}
cout << endl;
}
void test01()
{
char charArr[] = "badcfe";
int len = sizeof(charArr) / sizeof(char);
mysort(charArr, len);
printArry(charArr, len);
int intArr[] = { 7,5,1,3,9,7,2,5,6 };
len = sizeof(intArr) / sizeof(int);
mysort(intArr, len);
printArry(intArr, len);
}
int main()
{
test01();
return 0;
}
普通函数与函数模板的区别
-
普通函数调用时可以发生自动类型转换(隐式类型转换)
-
函数模板调用时,如果利用自动类型推导,不会发生隐式类型接换
-
如果利用显示指定类型的方式,可以发生隐式类型转换
#include<iostream>
using namespace std;
int myadd01(int a, int b)
{
return a + b;
}
template<typename T>
T myadd02(T a, T b)
{
return a + b;
}
void test01() {
int a = 10;
int b = 20;
char c = 'c';
cout << myadd01(a, c)<<endl;
cout << myadd02(a, b) << endl;
//自动类型推导时不会发生隐式类型转化
//cout << myadd02(a, c) << endl;
//显示指定类型会发生隐式类型转化
cout << myadd02<int>(a, c) << endl;
}
int main()
{
test01();
return 0;
}
总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T
普通函数与函数模板的调用规则
调用规则如下:
-
如果函数模板和普通函数都可以实现(即重名),优先调用普通函数
-
可以通过空模板参数列表来强制调用函数模板
-
函数模板也可以发生重载
-
如果函数模板可以产生更好的匹配,优先调用函数模板
总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
模板的局限性
局限性: 模板的通用性并不是万能的
#include<iostream>
using namespace std;
#include<string.h>
class Person
{
public:
string name;
int age;
Person(string name,int age):name(name),age(age){}
};
template<class T>
bool mycompare(T&a,T&b)
{
if (a == b)
{
return true;
}
else {
return false;
}
}
template<> bool mycompare(Person& a, Person& b)
{
if (a.name == b.name && a.age==b.age)
{
return true;
}
else {
return false;
}
}
void test01() {
int a = 10;
int b = 10;
bool ret = mycompare(a, b);
if (ret)
{
cout << "a==b"<<endl;
}
else {
cout << "a!=b"<<endl;
}
}
void test02()
{
Person p1("Tom", 18);
Person p2("Tom", 18);
bool ret = mycompare(p1, p2);
if (ret)
{
cout << "p1 == p2" << endl;
}
else {
cout << "p1!=p2" << endl;
}
}
int main()
{
test02();
return 0;
}
总结:
-
利用具体化的模板,可以解决自定义类型的通用化
-
学习模板并不是为了写模板,而是在STL能够运用系统提供的楼模板
类模板
语法
类模板作用: 建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表。
#include<iostream>
using namespace std;
#include<string.h>
template<class NameType,class AgeType>
class Person
{
public:
NameType name;
AgeType age;
Person(NameType name, AgeType age):name(name),age(age){}
void showperson() {
cout << this->name << endl;
cout << this->age << endl;
}
};
void test01() {
Person<string, int> p1("SuYou", 22);
p1.showperson();
}
int main()
{
test01();
return 0;
}
总结: 类模板和函数模板语法相似,在声明模板template后面加类,此类称为类模板
类模板与函数模板区别
类模板与函数模板区别主要有两点:
-
类模板没有自动类型推导的使用方式
-
类模板在模板参数列表中可以有默认参数
#include<iostream>
using namespace std;
#include<string.h>
template<class NameType,class AgeType=int>
class Person
{
public:
NameType name;
AgeType age;
Person(NameType name, AgeType age):name(name),age(age){}
void showperson() {
cout << "name:" << this->name << endl;
cout << "age:" << this->age << endl;
}
};
void test01() {
//类模板没有自动类型推导
//Person p1("SuYou", 22);
Person<string,int>p1("SuYou", 22);
p1.showperson();
}
void test02() {
//类模板在模板参数列表中可以有默认参数
Person<string>p1("Hello", 80);
p1.showperson();
}
int main()
{
test02();
return 0;
}
类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机是有区别的:
-
普通类中的成员函数一开始就可以创建
-
类模板中的成员函数在调用时才创建
类模板对象做函数参数
学习目标: 类模板实例化出的对象,向函数传参的方式
一共有三种传入方式:
-
指定传入的类型——直接显示对象的数据类型
-
参数模板化——将对象中的参数变为模板进行传递
-
整个类模板化——将这个对象类型模板化进行传递
#include<iostream>
using namespace std;
#include<string.h>
template<class T1, class T2>
class Person
{
public:
T1 name;
T2 age;
Person(T1 name,T2 age):name(name),age(age){}
void show() {
cout << "name: " << this->name <<endl << "age: " << this->age << endl;
}
};
//1.指定传入的类型
void printperson1(Person<string, int>& p)
{
p.show();
}
void test01() {
Person<string, int>p("Suyou", 22);
printperson1(p);
}
//2.参数模板化
template<class T1,class T2>
void printperson2(Person<T1, T2>& p)
{
p.show();
cout << "T1 type: " << typeid(T1).name() << endl;
cout << "T2 type: " << typeid(T2).name() << endl;
}
void test02() {
Person<string, int>p("Hello", 90);
printperson2(p);
}
//3.整个类都模板滑
template<class T>
void printperson3(T& p)
{
p.show();
cout << "T type: " << typeid(T).name() << endl;
}
void test03() {
Person<string, int>p("World", 99999);
printperson3(p);
}
int main()
{
//test01();
//test02();
test03();
return 0;
}
总结:
-
通过类模板创建的对象,可以有三种方式向函数中进行传参
-
使用比较广泛是第一种:指定传入的类型
类模板与继承
当类模板碰到继承时,需要注意一下几点:
-
当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
-
如果不指定,编译器无法给子类分配内存
-
如果想灵活指定出父类中T的类型,子类也需变为类模板
#include<iostream>
using namespace std;
template<class T>
class Base
{
public:
T m;
};
//class Son :public Base //错误,必须要知道父类中T的类型才能继承给子类
class Son:public Base<int>
{
};
void test01() {
Son s1;
}
//若想灵活指定父类中T的类型,子类也需要变成类模板
template<class T1,class T2>
class Son2 :public Base<T2>
{
public:
T1 obj;
Son2()
{
cout << "T1 type: " << typeid(T1).name() << endl;
cout << "T2 type: " << typeid(T2).name() << endl;
}
};
void test02()
{
Son2<int, char>S2;
}
int main()
{
test02();
return 0;
}
总结: 如果父类是类模板,子类需要指定出父类中T的数据类型
类模板成员函数类外实现
#include<iostream>
using namespace std;
#include<string.h>
template<class T1,class T2>
class Person
{
public:
//Person(T1 name,T2 age):name(name),age(age){}
Person(T1 name, T2 age);
T1 name;
T2 age;
void show();
};
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
template<class T1, class T2>
void Person<T1, T2>::show()
{
cout << name << age << endl;
}
void test01() {
Person<string, int>p("SuYou", 22);
p.show();
}
int main()
{
test01();
return 0;
}
总结: 类模板中成员函数类外实现时,需要加上模板参数列表
类模板分文件编写
学习目标: 掌握类模板成员函数分文件编写产生的问题以及解决方式
问题: 类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
-
解决方式1:直接包含.cpp源文件
-
解决方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
总结:主流的解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为.hpp
类模板与友元
学习目标: 掌握类模板配合友元函数的类内和类外实现
-
全局函数类内实现-直接在类内声明友元即可
-
全局函数类外实现-需要提前让编译器知道全局函数的存在
#include<iostream>
using namespace std;
#include<string.h>
//通过全局函数打印Person信息
//提前让编译器知道Person类存在
template<class T1, class T2>
class Person;
//全局函数在类外实现
template<class T1, class T2>
void print2(Person<T1, T2> p)
{
cout << "类外实现——name:" << p.name << endl << "类外实现——age:" << p.age << endl;
}
template<class T1, class T2>
class Person
{
public:
Person(T1 name,T2 age):name(name),age(age){}
private:
T1 name;
T2 age;
//全局函数类内实现
friend void print(Person<T1, T2> p)
{
cout << "name:" << p.name << endl << "age:" << p.age << endl;
}
//全局函数类外实现 加空模板参数列表 如果是全局函数是类外实现,需要让编译器提前知道这个函数存在
friend void print2<>(Person<T1, T2> p);
};
void test01() {
Person<string, int>p("SuYou", 22);
print2(p);
}
int main()
{
test01();
return 0;
}
总结: 建议全局函数做类内实现,用法简单,而且编译器可以直接识别
案例
案例描述:实现一个通用的数组类,要求如下:
-
可以对内置数据类型以及自定义数据类型的数据进行存储
-
将数组中的数据存储到堆区
-
构造函数中可以传入数组的容量
-
提供对应的拷贝构造函数以及operator=防止浅拷贝问题
-
提供尾插法和尾删法对数组中的数据进行增加和删除
-
可以通过下标的方式访问数组中的元素
-
可以获取数组中当前元素个数和数组的容量
#include<iostream>
using namespace std;
#include<string.h>
template<class T>
class MyArray
{private:
T* pAddress; // 指针指向堆区开辟的真实数组
int m_Capacity; // 数组容量
int m_Size; // 数组大小
public:
MyArray(int capacity):m_Capacity(capacity),m_Size(0),pAddress(new T[capacity]){
//cout << "MyArray 有参构造调用" << endl;
}
//MyArray(int capacity){
// cout << "MyArray 有参构造调用" << endl;
// this->m_Capacity = capacity;
// this->m_Size = 0;
// this->pAddress = new T[capacity];
//}
~MyArray()
{
//cout << "MyArray 析构调用" << endl;
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
}
}
MyArray(const MyArray& arr)
{
//cout << "MyArray 拷贝构造调用" << endl;
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//this->pAddress = arr.pAddress;浅拷贝的问题
//深拷贝
this->pAddress = new T[arr.m_Capacity];
//将arr中的数据拷贝过来
for (int i = 0; i < this->m_Size; i++) {
this->pAddress[i] = arr.pAddress[i];
}
}
//operator=防止浅拷贝问题
MyArray& operator=(const MyArray& arr)
{
//cout << "MyArray orperator=调用" << endl;
//先判断原来堆区是否有数据,如果you先释放
if (this->pAddress != NULL)
{
delete[] this->pAddress;
this->pAddress = NULL;
this->m_Capacity = 0;
this->m_Size = 0;
}
//深拷贝
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->pAddress = new T[arr.m_Capacity];
for (int i = 0; i < this->m_Size; i++) {
this->pAddress[i] = arr.pAddress[i];
}
return *this;
}
//尾插法
void Push_Back(const T& val)
{
//判断是否满了
if (this->m_Capacity == this->m_Size)
{
return;
}
this->pAddress[this->m_Size] = val; //在数组末尾插入数据
this->m_Size++; // 更新数组大小
}
//尾删法
void Pop_Back()
{
// 逻辑尾删,让用户访问不到最后一个元素
// 判断是否已经空了
if (this->m_Size == 0)
{
return;
}
this->m_Size--;
}
// 通过下标方式访问到数组中的元素
T& operator[](int index)
{
return this->pAddress[index];
}
//返回数组容量
int getCapacity()
{
return this->m_Capacity;
}
// 返回数组大小
int getSize()
{
return this->m_Size;
}
};
void printintArray(MyArray<int> & arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << arr[i] << endl;
}
}
void test01() {
MyArray<int>arr1(5);
//MyArray<int>arr2(arr1);
//MyArray<int>arr3(100);
//arr3 = arr1;
for (int i = 0; i < 5; i++)
{
arr1.Push_Back(i);
}
cout << "arr1的打印输出为:" << endl;
printintArray(arr1);
cout << "arr1的容量为:" << arr1.getCapacity() << endl;
cout << "arr1的大小为:" << arr1.getSize() << endl;
MyArray<int>arr2(arr1);
cout << "arr2的打印输出为:" << endl;
printintArray(arr2);
arr2.Pop_Back();
cout << "arr2尾删后:" << endl;
cout << "arr2的容量为:" << arr2.getCapacity() << endl;
cout << "arr2的大小为:" << arr2.getSize() << endl;
}
//测试自定义数据类型
class Person
{
public:
string name;
int age;
Person() {}
Person(string name, int age) :name(name), age(age) {}
};
void printpersonArray(MyArray<Person>& arr)
{
for (int i = 0; i < arr.getSize(); i++)
{
cout << "name:" << arr[i].name << " age:" << arr[i].age << endl;
}
}
void test02()
{
MyArray<Person> arr(10);
Person p1("孙悟空", 999);
Person p2("韩信", 22);
Person p3("东方耀", 19);
Person p4("妲己", 6666);
Person p5("猪八戒", 58);
arr.Push_Back(p1);
arr.Push_Back(p2);
arr.Push_Back(p3);
arr.Push_Back(p4);
arr.Push_Back(p5);
printpersonArray(arr);
cout << "arr的容量为:" << arr.getCapacity() << endl;
cout << "arr的大小为:" << arr.getSize() << endl;
}
int main()
{
//test01();
test02();
return 0;
}
STL初识
STL基本概念
-
STL(Standard Template Library,标准模板库)
-
STL从广义上分为: 容器(container) 算法(algorithm) 选代器(iterator)
-
容器和算法之间通过迭代器进行无缝连接
-
STL几乎所有的代码都采用了模板类或者模板函数
STL六大组件
STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
-
容器:各种数据结构,如vector、list、deque、set、map等,用来有放数据
-
算法:各种常用的算法,如sort、find、copy、for_each等
-
迭代器:扮演了容器与算法之间的胶合剂。
-
仿函数:行为类似函数,可作为算法的某种策略。
-
适配器:一种用来修饰容器或者仿函数或迭代器接口的东西a。
-
空间配置器:负责空间的配置与管理。
容器分为序列式容器和关联式容器两种:
-
序列式容器: 强调值的排序,序列式容器中的每个元素均有固定的位置
-
关联式容器: 二叉树结构,各元素之间没有严格的物理上的顺序关系
算法:问题之解法也 有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
算法分为:质变算法和非质变算法。
-
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
-
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
迭代器:容器和算法之间粘合剂
-
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而而又无需暴露该容器的内部表示方式。
-
每个容器都有自己专属的迭代器
-
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为为指针
迭代器种类:
常用的容器中迭代器种类为双向迭代器,和随机访问迭代器
容器算法迭代器初识
vector存放内置数据类型
-
容器: vector
-
算法: for_each
-
迭代器: vector<int>