凡人视角C++之string(上)

  好久没有更新博客了,这段时间一直在忙图像处理的项目,最近空了下来,也是时候整合C++的相关内容,静心感受下编程语言的魅力,和大家共同探讨学习。我将以头文件的形式展开学习,且只讲述相关接口的应用,至于内部具体的实现,鉴于本人水平有限,不敢献丑。经过考虑,决定先从和数据结构相关的头文件开始,因为这些头文件里的内容在OJ里经常要用到。今天要学习的是string头文件,这里的string是C++里的string,而C里的string在C++里仍然保留,头文件名为cstring,这里不做展开。在此说明:以下的代码都是在VS 2015编译环境下运行。

Some Details

basic_string  

  实际上,string头文件里并不只有string这个类型,这个头文件里最主要的是一个叫basic_string的类模板,这个类模板的声明如下:

template < class charT,
           class traits = char_traits<charT>,
           class Alloc = allocator<charT>
           > class basic_string;

  上面的类模板构造有点复杂,我们没有必要深究,只需要知道我们可以在basic_string这个类中自定义三种类型作为member type就行了。其中的charT (character type)是最重要的模板参数,它直接说明了字符串中的元素类型,另外两个类型均采用默认参数(以charT为参数构建的类)即可。

  为什么要提basic_string这玩意儿呢,因为string类型正是basic_string的special type,在实际操作中,最常使用的是char类型(字符型),故只需将上面的charT转换为char便是string类型了。string类型的构造如下:

typedef basic_string<char> string;      //元素为8bits字符类型

  类似的,在c++内置类型中,还有wstringu16stringu32string(后两个是在c++11标准下),我们可以根据实际需要选取字符串类型,它们的构造如下:

typedef basic_string<wchar_t> wstring;      //16位或32位
typedef basic_string<char16_t> u16string;   //16位
typedef basic_string<char32_t> u32string;   //32位

String versus Character Array

  之前看到过一篇文章,讲到了string和char*,我在此做个总结。

  1. string内存由系统进行管理,自动实现内存的申请和释放;而char* 则需要自己管理。如果你要使用的内存较大,则string系统自动申请的内存可能不够使用。因此,在使用内存大小知道的情况下,建议使用char* ,而当使用内存具体大小未知,但内存不大时可以使用string。
  2. string相比char* 有一个优点,c++标准库对string类进行了封装,里面的各种成员函数处理字符串相当方便,这也是我常用string的一个原因。

    至于string和char类型的vector有什么区别,我倒还没有深究过,大概是成员函数不同吧,知道的小伙伴可以在下面留言下。

Elaboration

Member Functions

string::string
string();       //(1)默认构造函数,构造一个空字符串,字符串长度为0
string(const string& str);      //(2)拷贝构造函数
string(const string& str, size_t pos, size_t len = npos);
//(3)复制str一部分,pos是起始位置,len是复制的字符串长度(默认值是到字符串底部)这里要注意str的第一个字符pos=0
string(const char* s);  //(4)复制C模式下s指针指向的字符数组(字符串)
string(const char* s, size_t n);    //(5)复制前n个字符数组元素
string(size_t n,char c);        //(6)复制n个连续的字符c
template <class InputIterator>
  string(InputIterator first, InputIterator last);
//(7)利用iterator复制字符序列,范围为[first,last),注意最后一个不包括
string(initializer_list<char> il);  //(8)将初始化列表il转换为string
string(string&& str) noexcept; //(9)右值引用str,并且不抛出异常信息。

  关于右值引用和move语义这块内容,是C++11的新特性,我当时琢磨了很久也还是云里雾里,估计是自己悟性不够高,我觉得有两篇文章写得还不错,贴在这里。另外,不知右值和临时变量有何不同,知道的小伙伴也可以在下面留言。

https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/

https://my.oschina.net/letiantian/blog/470921

Demonstration:      //为简略,今后代码将省略cout,自己操作时应加上
#include<iostream>
#include<string>        //调用string头文件
#include<initializer_list>  //(8)中要用到
using namespace std;

string test(const string& x)    //(9)的测试函数,产生右值
{
    return x;
}

int main()
{
    string s4("string now");    //(4)
    string s1;      //(1)
    string s2(s4);  //(2)
    string s3a(s4, 7, 3);   //(3)设置长度
    string s3b(s4, 7);  //(3)默认长度,也就是到底部
    string s5("string now", 6); //(5)
    string s6(3, ‘6‘);  //(6)
    string s7(s4.begin(), s4.begin() + 6);
    //(7)begin函数后面会提到,可以生成iterator
    initializer_list<char> s0 = {‘s‘, ‘t‘, ‘r‘, ‘i‘, ‘n‘, ‘g‘};
    //生成initializer_list
    string s8(s0);  //(8)
    string s9(test(s4));    //(9)
}
//自行将注释中的序号与上面的序号对照进行学习

  结果如下,看看是不是和你们想象中的一样呢。

string::operator=
string& operator= (const string& str);  //(1)赋值string类型变量
string& operator= (const char* s);      //(2)赋值c风格字符串
string& operator= (char c);             //(3)赋值一个字符,字符串长度为1
string& operator= (initializer_list<char> il); //(4)赋值初始化列表il
string& operator= (string&& str) noexcept;  //(5)利用右值引用赋值
Demonstration:
#include<iostream>
#include<string>        //调用string头文件
#include<initializer_list>  //(4)中要用到
using namespace std;

string test(const string& x)    //(5)的测试函数,产生右值
{
    return x;
}

int main()
{
    string s1, s2, s3, s4, s5;
    //为把operator=和构造函数分开,使用默认构造函数
    s2 = "string now";      //(2)
    s1 = s2;                //(1)
    s3 = ‘6‘;               //(3)
    initializer_list<char> s0 = {‘s‘, ‘t‘, ‘r‘, ‘i‘, ‘n‘, ‘g‘};
    s4 = s0;                //(4)
    s5 = test(s2);          //(5)
}

Some More Details

string类型对象是以’\0’结尾的嘛?

  估计很多人跟我一样都想过这个问题,因为在C语言中,系统默认字符串都是以’\0’截尾的。我们不妨今天来编个小程序验证一下。

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string s0 = "string now";   //初始化s0

    if (s0[10] == ‘\0‘)
        cout << "Yes." << endl; //如果末尾是‘\0‘,则输出Yes.
    else cout << "No." << endl; //如果末尾不是‘\0‘,则输出No.

    return 0;
}

  在这里,我们得到了一个令人满意的答案,说明C++风格的string仍然是以’\0’结尾的,通常情况下,末尾的’\0’我们可以忽略不计。

编码类型对string的影响

  提到这个问题,是因为我注意到了这么一句话,我在这里引用一下。

Note that this class handles bytes independently of the encoding used: If used to handle sequences of multi-byte or variable-length characters (such as UTF-8), all members of this class (such as length or size), as well as its iterators, will still operate in terms of bytes (not actual encoded characters).

  这句话的意思概括起来就是说,不管你使用哪种类型的编码,string类型的操作都将按一个字节的方式来处理。我们不禁想到了中文,因为中文是在GB2312中编码的,而且一个中文字符占用两个字节,这样会引起什么问题呢?我们也来测试一下。

#include<iostream>
#include<string>
using namespace std;

int main()
{
    string s0 = "测试";   //用中文初始化s0

    cout << s0 << endl << endl; //查看能否输出中文

    for (int i = 0; i < 2; ++i) //输出前2个字节,看看是否会输出测试两个字
        cout << s0[i] << endl;

    return 0;
}

  刚开始,我们将整个字符串输出,看看是否会显示“测试”这两个字,结果是这两个字显示了,说明c++是可以识别GB2312编码的。而当我们尝试将字符串按单个字节输出的时候,就发现了问题,因为输出台输出的是空白,这就说明string类型并不能直接辨别这个是什么编码,它只能按单个字节进行读取,这就会导致该字节没有默认的ASCII编码可以对应,因此也就不会有内容输出了。

  string的内容实在有点多,为了保持页面的精简,方便浏览,我将把下半部分内容放到下篇博文中。如有错误,欢迎指正!

时间: 2024-10-31 09:34:01

凡人视角C++之string(上)的相关文章

凡人视角C++之string(下)

上篇文章中,我们着重引入了string类型,也谈及了string类型构造函数和赋值函数的应用,今天我们就来谈谈string类型对象可以利用哪些C++内置的函数.另外,文章有点长,如果大家想方便点,可以在目录里查找. Elaboration Iterators begin //string::begin iterator begin() noexcept; //返回默认的迭代器类型对象 const_iterator begin() const noexcept; //返回常量迭代器类型对象 beg

java基础面试题:switch语句能否作用在byte上,能否作用在long上,能否作用在String上?

package com.swift; public class Switch_Test { public static void main(String[] args) { /* * switch语句能否作用在byte上,能否作用在long上,能否作用在String上? */ byte zijie = 3; System.out.println(zijie); long changzheng=3; switch (changzheng) { //cannot switch on a value

Java面试题:switch能否作用在byte、long、String上?

switch可作用于char byte short int switch可作用于char byte short int对应的包装类 switch不可作用于long double float boolean,包括他们的包装类 switch中可以是字符串类型,String(JDK1.7之后才可以作用在string上) switch中可以是枚举类型(JDK1.5之后) public class Test { public static void main(String[] args) { // swi

switch语句能否作用在byte,long,string上

switch是java中的多分支结构.在switch(expr)中,expr只能是一个整数表达式,或者是枚举常量,整数表达式可以是int基本类型也可以是Integer包装类型,由于byte,short,char都可以隐含的转为int,所以这些类型以及这些 类型的包装类型都是可以的,而long,string都不符合switch的语法规则并且都不能隐含的转为int,所以是不能作用于switch语句. 原文地址:https://www.cnblogs.com/jasonboren/p/11055024

Java企业微信开发_08_JSSDK多图上传

一.本节要点 1.1可信域名 所有的JS接口只能在企业微信应用的可信域名下调用(包括子域名),可在企业微信的管理后台“我的应用”里设置应用可信域名.这个域名必须要通过ICP备案,不然jssdk会配置失败 1.2JS-SDK使用权限签名算法 1.2.1 签名生成规则如下: (1)参与签名的字段包括: noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) . (2)对所有待签名参数按照字段名的ASCII

HTML5 大文件上传示例

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>HTML5大文件分片上传</title> <script src="js/spark-md5.min.js" type="text/javascript" charset="utf-8"></script> <

struts2 文件的上传下载 表单的重复提交 自定义拦截器

文件上传中表单的准备 要想使用 HTML 表单上传一个或多个文件 须把 HTML 表单的 enctype 属性设置为 multipart/form-data 须把 HTML 表单的method 属性设置为 post 需添加 <input type=“file”> 字段. Struts 对文件上传的支持 在 Struts 应用程序里, FileUpload 拦截器和 Jakarta Commons FileUpload 组件可以完成文件的上传. 步骤:1. 在 Jsp 页面的文件上传表单里使用

PHP文件的上传下载

一.文件的上传 1.客户端设置: (1).在 标签中将enctype和method两个属性指明相应的值. Enctype="multipart/form-data"; Method="POST" (2).form表单中设置一个hidden类型的input框,其中name的值为MAX_FILE_SIZE的隐藏值 2.服务器端设置: (1).$_FILES多维数组:用于存储各种上传文件有关的信息 (2).文件上传与php配置文件的设置,如以下php.ini文件中的一些指

富头像上传编辑器文档(from www.sysoft.cc)

调用方法 new fullAvatarEditor(swfContainerID, [height], [width], flashvars, [callback]); 返回值:object,该对象可调用call方法,请参见 call方法. swfContainerID 用以包裹Flash的HTML元素的ID. height Flash的高度,默认为 600. width Flash的宽度,默认为 630. flashvars 配置参数 名称 类型 默认值 描述 id String fullAv