C++11基础学习系列二

时间:2022-05-07
本文章向大家介绍C++11基础学习系列二,主要内容包括概述、string、string对象操作、vector、迭代器、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

概述

在C++11基础学习系列一中介绍一些c++11一些基础知识。基础学习系列二进一步讲解C++11.

string

string不可思议,在C++中是字符串类库。如何初始化类的对象是由类本身决定的。类可以定义很多种初始化对象的方式。比如:

#include <iostream>
#include <string>

int main() {
	//默认初始化,s1是一个空字符串。
    std::string s1;
	//s2是s1的副本
    std::string s2(s1);
	//等价于s3(s1),拷贝初始化。
    std::string s3 = s1;
	//s4是字面值的副本,直接初始化
    std::string s4("value");
	//s5拷贝初始化
    std::string s5 = "value";
	//s6是一个包含有10c的字符串,直接初始化。
    std::string s6(10,'c');
    return 0;
}

1.使用=在初始化对象,实际上执行的是拷贝初始化,编译器把等号右边对象的初始值拷贝到新创建的对象;如果不使用=,则执行的是直接初始化。 2.当初始值只有一个时,拷贝初始化或直接初始化都可以;当多于一个时一般都是采用的直接初始化。比如(s6)。

string常用函数

1.getline读取一行字符串处理函数,包含输入时的空白符。 2.empty判断字符串是否为空 3.size字符串对象的长度,size返回的是string::size_type,它也是无符号类型的值并且是与机器无关的特性。在c++11标准里面,允许编译器通过auto或者decltype来推断变量的类型。由于是无符号类型,切记与有符号混合使用,会带来一些非确定结果。

#include <iostream>
#include <string>


int main() {
    std::string s1;
    while (getline(std::cin,s1)){
        if(!s1.empty()) {
		    auto len = s1.size();
            std::cout << s1<<" the size is "<<len<< std::endl;
        } else{
            std::cout << "the input string is empty"<<std::endl;
        }
    }
    return 0;
}

string对象操作

两个字符串相加大致分为两种情况,并且差异很大:

  • 两个string对象直接相加。
  • 字面值和string对象相加。

对于第一种情况完全支持,没有什么特别的。第二种情况需要注意一些,C++标准库允许字面值把字符串字面值转化为string对象。字符串和string对象进行相加时,至少保证+两侧运算对象至少有一个是string对象。

#include <iostream>
#include <string>

int main() {
    std::string s1="hello",s2="work";
	//没有问题,先进行(s1+",")得到的是一个string对象,然后新的字符串对象再和s2相加。
    std::string s3 = s1+","+s2;
	//也是没有问题的
    std::string s4 = s3+",";
	//错误,字符串字面值不能直接相加。
    std::string s5 = "hello"+",";
	//先进行(s1+",")得到一个新的string对象,string对象在和"hello相加。"
    std::string s6 = s1+","+"hello";
	//错误,字符串字面值无法进行直接相加
    std::string s7 = "hello"+"world"+s1;
    return 0;
}

C++标准库不仅包含C++语言特有的功能外,也兼容C语言的标准库。C语言的标准库形如name.h,C++将这些文件命名为cname。 在处理字符串操作时,不得不介绍range for,用于遍历的。

#include <iostream>
#include <string>

int main() {
    std::string s1="hello";
    for(auto i : s1){
        std::cout<< i <<std::endl;
    }
    std::cout << s1<<std::endl;
    for(auto &c : s1){
        c = toupper(c);
    }
    std::cout << s1<<std::endl;
    return 0;
}

如果只处理一部分字符或者说通过索引来处理,比如:

#include <iostream>
#include <string>

int main() {
    std::string s1("hello world");
    for (decltype(s1.size()) index = 0;index != s1.size() && !isspace(s1[index]);++index){
        s1[index] = toupper(s1[index]);
    }
    std::cout<<s1<<std::endl;
    return 0;
}

vector

vector是容器的一种类型,用于存放对象的集合。它在数据结构上的表示为单链表,也是C++的类模板。模板可以看做是编译器生成类或者函数编写的一份说明,编译器根据模板创建类或函数的过程(实例化)。vector是模板而非类型。 初始化vector对象有很多种方式: 1.默认初始化 2.拷贝初始化 3.直接初始化 3.列表初始化

我们以此来看一下:

#include <iostream>
#include <vector>


int main() {
	//默认初始化,int默认为0
    std::vector<int> test1;
	//直接初始化
    std::vector<int> test2(test1);
	//拷贝初始化
	std::vector<int> test6 = test1;
	//列表初始化
    std::vector<int> test3={1,2,3,4};
	//通过列表里面的元素值,对vector进行列表初始化。
    std::vector<int> test4{2, 3, 4, 5};
    return 0;
}

指定元素数量来初始化vector,比如:

#include <iostream>
#include <vector>
#include <string>

int main() {
	//10个整数元素,每个元素初始化为-1.
    std::vector<int> test(10,-1);
	//10个整数元素,每个元素初始化为0.
    std::vector<int> test1(10);
	//10个字符串元素,每个元素初始化为"hiahi"
    std::vector<std::string> test2(10, "hiahi");
    return 0;
}

vector初始化重点:

  • 如果使用的(),是通过提供的元素数量和值/默认初始值来构造vector。比如:std::vector test(10,-1);std::vector test1(10)。
  • 如果使用的{},可以理解为是通过{}里的值进而通过列表初始化来构造vector对象。比如:std::vector test4{2, 3, 4, 5}。初始化过程会尽可能地把{}里面的值当成是元素初始值的列表来处理。,只是如果无法执行列表初始化时,就会考虑其它的方式进行初始化。
#include <iostream>
#include <vector>
#include <string>

int main() {
	//10个字符串对象,每个元素初始化为hihi.
    std::vector<std::string> test(10,"hihi");
	//只有一个hihi元素初始化vector。
    std::vector<std::string> test1{"hihi"};
	//错误的,无法直接通过字面值常量初始化。
    std::vector<std::string> test2("hihi");
	//10个值为hihi的vector
    std::vector<std::string> test3{10,"hihi"};
	//10个为空的字符串的vector
    std::vector<std::string> test4{10};
    return 0;
}

vector增加元素采用尾部增加,这样的追加元素才是最高效的。值得注意的是不要在for范围里面或者循环里面改变vector的size。


#include <iostream>
#include <vector>
#include <string>

int main() {
    std::vector<std::string> test{"test1","test2","test3"};
    test.push_back("test4");
    for(auto i : test){
        std::cout << i << std::endl;
    }
    return 0;
}
#输出为
test1
test2
test3
test4

迭代器

所有标准的容器类型都可以使用迭代器(string不是容器,但是string支持很多与容器相似的操作包括迭代器。) 迭代器不像指针使用取地址符号,而是采用begin和end方法。begin指向了第一个元素位置,end指向了尾元素的下一个位置,该位置并不存在只是一个标记。如果容器为空,那么begin和end都指向同一个位置。


#include <iostream>
#include <vector>
#include <string>

int main() {
    std::vector<std::string> test{"test1","test2","test3"};
    for(auto start = test.begin();start!=test.end();++start){
        std::cout << *start << std::endl;
    }
    return 0;
}

常用的操作:

  • *iter 返回迭代器iter的引用
  • iter->mem 解引用iter并获取名为mem的成员,等价于(*iter).mem
  • ++iter 指向下一个元素
  • —iter 指向上一个元素
  • iter1==iter2,两个迭代器是否指向同一个元素。

在C++11版本中增加了两个迭代器类型分别为cbein和cend返回的const_iterator.,而begin和end返回的iterator类型。最主要的区别是:begin和end返回的是即可读又可写的元素类型,而cbegin和cend返回的是只读的元素类型,cbegin和cend是非常有用的,它可以避免你修改元素的风险。


#include <iostream>
#include <vector>
#include <string>

int main() {
 const std::vector<std::string> test1{"test1","test2","test3"};
    for(auto start = test1.cbegin();start!=test1.cend() && !start->empty();++start){
        std::cout << *start << std::endl;
    }
	# 获得元素个数
	auto start = test1.cbegin(),end=test1.cend();
	# 值得注意
    std::cout << end-start << std::endl;
    return 0;
}

参考书籍

《C++ Primer》