1.string类
string类是由头文件string支持的,要使用类,关键要知道它的公有接口。
1.1构造字符串
#include<iostream>
#include<string>
using std::string;
using std::cout;
using std::endl;
int main()
{
string one("Lottery Winner!");
string two(20, '-');
string three(one);
string four;
const char* s = "All's well that ends well";
char sc[] = "All's well that ends well";
string five(s, 20);
string six(sc + 5, sc + 10);
string six_2(&five[5], &five[10]);
string seven = { 'L','O','P','T' };
cout << one << endl << two << endl
<< three << endl << four << endl << five << endl
<< six << endl << six_2 << endl << seven << endl;
one[0] = 'P';
three = one + two;
cout << one << endl << three << endl;
return 0;
}
注:数组名相当于指针,所以:
string six(sc + 5, sc + 10);
而对象名不是指针,所以要下标索引加取地址符号:
string six_2(&five[5], &five[10]);
移动构造函数再第18章讲解,最后一个构造函数使得能够将列表初始化方法用于string类,本章后边更深刻讨论initializer_list。
1.2string类输入
1.2.1C风格
3种输入方式:
char info[100];
cin >> info;
cin.getline(info, 100);
cin.get(info, 100);
1.2.2string对象
2种方式:
string lname;
cin >> lname;
getline(cin, lname);
string具有自动调整大小的功能,不需要指定长度,并且string类,所以cin作为参数。
string函数停止读入条件:
1.到达string对象最大允许长度:(string::npos或可供分配的内存字节数中较小的一个)
2.到达文件末尾
3.遇到分界字符(默认为\n)
对于>>输入字符,程序在读到空白字符(空格 换行 制表)就会停止。
#include<iostream>
#include<string>
#include<fstream>
#include<cstdlib>
using namespace std;
int main()
{
ifstream fin;
fin.open("C:\\Users\\HXY\\Desktop\\abc.txt");
string item;
int count = 0;
getline(fin, item, ':');
while (fin)
{
++count;
cout << count << ":" << item << endl;
getline(fin, item, ':');
}
cout << "Done!";
fin.close();
return 0;
}
当指定了分界字符后,换行符就成了普通字符。
第4行第一个是换行符。
1.3使用字符串
1.字符串可以用6个关系运算符直接比较;
2.类方法 size()和length()返回字符串长度;
3. = += 赋值和附加字符串;
4.查找方法:
现在不返回std::npos,而是返回-1.(2022年)
rfind()方法和find()类似只不过查找最后一次出现的位置;
find_first_of()查找参数中任何一个字符第一次出现的位置;
find_last_of()查找参数中任何一个字符最后一次出现的位置;
find_first_not_of()查找不在参数中的任何一个字符第一次出现的位置;
find_last_not_of()查找不在参数中的任何一个字符最后一次出现的位置;
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1("hark");
string s2("cobra");
string s3 = "shark";
int i = s3.find(s1, 0);
int j = s2.find('b', 0);
int j2 = s2.find('j', 0);
int k = s3.find_first_of(s2);
cout << i << endl
<< j << endl
<< j2 << endl
<< k << endl;
return 0;
}
1.4string其他功能
方法capacity()返回当前分配给字符串的内存块大小,reserve()请求内存块的最小长度。
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1("hark");
string s2("cobra");
string s3 = "shark";
cout << "size:\n";
cout << s1.size() << endl << s2.size() << endl << s3.size() << endl;
cout << "capacity:\n";
cout << s1.capacity() << endl << s2.capacity() << endl << s2.capacity() << endl;
s2.reserve(70);
cout << s2.capacity();
return 0;
}
c_str()方法返回一个指向C风格字符串的指针,内容与对象相同。
1.5字符串种类
模板basic_string有4个具体化,除了使用char类型,还可以使用wchar_t,char16_t,char32_t.
2.智能指针模板类
2.1智能指针的引入
智能指针是行为类似于指针,但是是一个类对象,可用于帮助管理动态内存分配的智能指针模板。
智能指针模板的存在是为了在存在动态分配函数终止(正常或异常),使得动态分配的内存清除掉,需要一个析构函数,所以智能指针就出现了。
2.2使用智能指针
以下三个智能指针模板(auto_ptr、unique_prt、shared_ptr)位于名称空间std中,定义在头文件memory中,使用通常的模板语法来实例化所需类型的指针,我们便不需要记住稍后释放这些内存。
auto_ptr<double> pd(new double);//使用指向double的内存地址来初始化智能指针对象
auto_ptr<string> ps(new string);
#include<iostream>
#include<memory>
#include<string>
using namespace std;
class report
{
private:
string str;
public:
report(const string s) :str(s) { cout << "Object created.\n"; };
~report(){ cout << "Object deleted.\n"; }
void comment() { cout << str; }
};
int main()
{
{
auto_ptr<report> p1(new report("using auto_ptr"));
p1->comment();
}//离开代码块,指针将过期
{
unique_ptr<report> p2(new report("using unique_ptr"));
p2->comment();
}
{
shared_ptr<report> p3(new report("using shared_ptr"));
p3->comment();
}
return 0;
}
智能指针很多地方都类似于常规指针,解引用、->访问结构成员,赋值给相同类型的常规指针(显式类型转换),赋值给同类型的智能指针。无论如何,都不能将智能指针用于非动态存储的对象或变量。
显式类型转换:
shared_ptr<double> p1;
double* p2 = new double;
p1 = p2; 不允许
p1 = shared_ptr<double>(p2); 这才正确
shared_ptr<double> pshared = p2; 不允许
shared_ptr<double> pshared(p2); 这才正确(复制构造函数)
return 0;
2.3有关智能指针的注意事项
智能指针在复制的时候,如果执行浅复制,则会导致内存释放两次,严重错误。
shared_ptr创建智能更高的指针,跟踪引用特定对象的智能指针数,仅当最后一个指针过期时,才会调用析构函数;
auto_ptr 和 unique_ptr会建立所有权概念,对于特定的对象,只能有一个指针指向它,如果进行赋值操作,则所有权会转移,auto_ptr被转移的所有权的指针指向空指针,不安全;而unique_ptr不允许赋值操作,除非存在一个临时对象,临时对象的所有权转让给新对象,老对象被销毁,这时赋值是可以用的。
unique_ptr可用于数组变体。new delete、new[] delete[]要配合使用,unique_ptr可以满足这种情况。
2.4选择智能指针
如果程序要使用多个指向同一个对象的指针,用shared_ptr;否则用unique_ptr(auto_ptr被C++11抛弃)。
3.标准模板库(STL)
STL提供了一组表示容器(存储同类型若干值)、迭代器(遍历容器的广义指针)、函数对象(类似于函数的对象)和算法(完成特定任务)的模板。STL不是面对对象的编程,而是泛型编程。
3.1模板类vector
vector在头文件vector中定义里模板,存储了一组可以随机访问的值,使用索引来直接访问元素。要创建vector模板对象,通常<type>指出要使用的类型,可以使用动态内存分配,可以使用变量来指出需要多少矢量。
vector<double> p1(5);
int n;
cin >> n;
vector<string> p2(n);
默认使用new和delete来动态分配内存,可以选择分配器(STL模板容器接受可选的模板参数)。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int main()
{
vector<int> ratings(5);
vector<string> titles(5);
for (int i = 0; i < 5; i++)
{
cout << "Enter title #" << i + 1 << ":";
getline(cin, titles[i]);
cout << "Enter your ratings:";
cin >> ratings[i];
cin.get();
}
for (int i = 0; i < 5; i++)
{
cout << titles[i] << endl;
cout << "Ratings:" << ratings[i] << endl;
}
}
3.2可对矢量执行的操作
所有的STL容器都提供了一些基本的方法:
size()——返回容器元素数目
swap()——交换两个容器的内容
begin()——返回一个指向容器中第一个元素的迭代器
end()——返回一个表示超过容器尾的迭代器
迭代器:广义指针,可以对其使用解引用和递增,迭代器的存在让STL存在能够为各种不同的容器类提供统一的接口。每个容器都定义了一个合适的迭代器,该迭代器的类型是一个名为iterator的typedef,作用域为整个类。
vector<int>::iterator pd;
pd = ratings.begin();
pd[2] = 23;
pd++;
auto存在让一切变得简单
auto p = ratings.begin();
所有容器都有上述方法,vector包含一些只有某些STL容器才有的方法。
push.back()将元素添加到矢量的末尾,在编写或运行程序时,无需了解元素数目。
erase()删除矢量中给定区间的元素,接受两个迭代器参数。
insert()在矢量中插入元素;,接受三个迭代器参数,第一个为插入的位置。
#include<iostream>
#include<vector>
#include<string>
using namespace std;
struct Review
{
string title;
int rating;
};
bool FillReview(Review& rr);
void ShowReview(const Review& rr);
int main()
{
vector<Review> books;
Review temp;
while (FillReview(temp))
{
books.push_back(temp); //添加元素
}
int num = books.size(); //返回长度
auto pd = books.begin(); //指针
f