C++ Primer 3章 字符串、向量和数组

  • using声明既可以直接声明命名空间 using namespace std,也可以声明命名空间里面的某个方法using std::cin。

标准库类型string

//使用=初始化一个变量,是拷贝初始化,编译器把等号右侧初始值拷贝到新对象中去。
string s0;//s0默认为空字符。
string s3 = "value";//s3是"value"的副本,出字面值最后一个空字符外。
string s4 = string(10,'c');//拷贝初始化。

//不使用等号的就是直接初始化
string s6("value");//和s3一样
string s7(10 , 'c');//s7是10个c
int main()
{
    string s;
    cin >> s;
    cout << s << endl;
    exit(0);
}
//先输入一段字符串,string对象自动忽略开头的空白并从第一个字符串读到下一处空白为止。先输入然后enter开始读取。

hello world
hello
按 来关闭窗口…

int main()
{
    string s;
    string s1;
    while( getline(cin , s1) )
        cout << s1 << endl;
    exit(0);
}
//从输入流读取内容,直到碰到换行符(enter),换行符也读进来了存到s1中(不存输入流中的换行符)。返回流参数,所以可以判断用。
因为s1不存在换行符,所以手动加endl换行并刷新缓冲区。
  • 比较string对象利用字典顺序:直接从第一字符的开始比较,直到碰到第一个大小不一样的字符为止。
  • 两个string对象相加,直接将对应的字符连在一起赋值给新的string对象。
  • string对象和字符字面值相加必须确保+号两侧至少有一个string对象。string s6 = (s1 + “, “) + “world”;正确。因为字符串字面值并不是string对象,为了和C语言兼容。其实加号暗中含有强制转换。
/** @file include/cctype
 *  This is a Standard C++ Library file.  You should @c \#include this file
 *  in your programs, rather than any of the @a *.h implementation files.
 *
 *  This is the C++ version of the Standard C Library header @c ctype.h,
 *  and its contents are (mostly) the same as that header, but are all
 *  contained in the namespace @c std (except for names which are defined
 *  as macros in C).
 */
 //
// ISO C++ 14882: 
//
#pragma GCC system_header

#include 
#include  //这里直接包含C库文件

#ifndef _GLIBCXX_CCTYPE
#define _GLIBCXX_CCTYPE 1

// Get rid of those macros defined in  in lieu of real functions.
#undef isalnum
#undef isalpha
#undef iscntrl
#undef isdigit
#undef isgraph
#undef islower
#undef isprint
#undef ispunct
#undef isspace
#undef isupper
#undef isxdigit
#undef tolower
#undef toupper

namespace std//然后将c库文件里面函数封装到一个命令空间里面去,这就是c库和对应c++库的不同之处。
{
  using ::isalnum;
  using ::isalpha;
  using ::iscntrl;
  using ::isdigit;
  using ::isgraph;
  using ::islower;
  using ::isprint;
  using ::ispunct;
  using ::isspace;
  using ::isupper;
  using ::isxdigit;
  using ::tolower;
  using ::toupper;
} // namespace std

#if __cplusplus >= 201103L

#ifdef _GLIBCXX_USE_C99_CTYPE_TR1

#undef isblank

namespace std
{
  using ::isblank;
} // namespace std

#endif // _GLIBCXX_USE_C99_CTYPE_TR1

#endif // C++11

#endif
  • c语言头文件在c++中修改了一点:其一去掉了.h并且在头文件名字前面加了c;其二c++在里面加入了命名空间。所以ctype.h和cctype里面的内容差不多。
    在C++中使用c库应该尽量使用cctype的形式,因为这样不用牢记哪些是从c语言里面继承过来,哪些又是c++独有。因为它的头文件把独有和继承放在了一些,你可以直接使用了,没有关系哦。
    例如我要利用Linux系统函数进行socket编程,我可以在包含linux头文件的基础上,自己利用它的系统调用写一个类,这是没有问题的。很方便,很有用。这就是c++和c语言的强大之处。两者分界线不明显。再次论证c++是c的超集。
int main()
{
    string s("string  World!!!");
    cout << s << endl;
    decltype(s.size()) count = 0;//返回值的类型定义变量
    for(auto &c : s){//对于s中的每一个字符,执行下面的某种操作
        if(ispunct(c))//实际运用了c标准库
            count++;
        c = toupper(c);//c是引用,改变string字符,必须使用引用.
    }
    cout << count << endl;
    cout << s << endl;
    if(!s.empty())//使用下标索引修改单个字符,类里面定义了该方法.
        s[0] = 'W';//下标类型是string::size_type类型.就是无符号整形,为了排除在不同机器上面的差异
    cout << s << endl;
    for(decltype(s.size()) index = 0 ; index < s.size() && s[index] != ' ';index++)
        s[index] = '1'; //使用迭代器修改字符
    cout << s << endl;
    exit(0);
}
  • c++11新标准中定义了使用for循环处理每一个字符,每次迭代c被初始化为s的下一个元素值。decltype的作用在上一章已经说明清楚了,利用返回值定义类型。
  • 要for循环改变string对象字符值而不用迭代的方法,必须利用引用。
  • s.size()的类型是string::size_type类型,这是string类里面自定义类型为什么需要有这种类型呢?而不去直接使用int、long等等呢?大多数标准库或者Linux内核里面都定义了几种配套的类型,这些配套的类型与具体机器就无关了。例如我存储一些数值只需要4字节,少了存储不够,多了浪费空间。假如我直接用了int定义,那么可能不同机器上面int所代表的字节不一样。这样定义之后,所有变量定义都需要重新更改成4字节类型。但是我可以直接typedef new_type int;然后用new_type去定义需要4字节的变量,不同机器上面只需要去修改int成相应的类型即可,其他用new_type定义的类型不用更改,屏蔽了不同机器上面的差异。这种方法在Linux内核,以及标准库里面大量用到,应该清楚明白。。
  • 变量应该在使用的地方再定义。迭代必须注意下标的上限是多少。

标准库类型vector

  • vector表示对象集合,所有对象类型相同,也称为容器。可以容纳其他对象,将在后面详细介绍,现在学会用用即可。vector输入类模版。实例化的时候必须提供何种类型。vector v;进行定义。
//列表初始化,初始化几个元素向量,利用 { }
vector<string> str = {"a" , "an" , "the"};//3个元素列表初始化
vector<string> str1{"a" , "an" , "the"};//3个

vector<int> in = {1 , 2 , 3};//3个元素列表初始化
vector<int> in{1 , 2 , 3};//3个元素列表初始化

//指定数量初始化,初始化指定个数(必须有个数)向量,利用 ( ) 
vector<string> s(10 , "hi");//10个string,指定数量初始化
vector<string> s(10);//10个string默认为空,指定数量初始化

vector<int> in(10 , -1);//10个int类型每个是1,指定数量初始化
vector<int> in(10 );//10个int类型每个是0,指定数量初始化

//两种特殊的情况,由编译器根据实际情况选择初始化。
vector<string> v{10};//执行指定数量初始化10个string默认为空,指定数量初始化
vector<string> v{10 , "hi"};//执行指定数量初始化10个string默认为空,指定数量初始化,因为有一个整形。
  • c++11给vector提供了两种初始化方法,其一是列表初始化,其二就是指定数量初始化。
int main(void)
{
    vector<int> Vnumber;
    int num;
    while (cin >> num) {
        Vnumber.push_back(num);
    }
    for(decltype(Vnumber.size()) index = 0 ; index < Vnumber.size();index++ )
        cout << Vnumber[index]  + Vnumber[Vnumber.size() - 1 - index] << endl;
    exit(0);
}
  • 下标索引问题:下标运算符只能访问已经存在的元素并且访问不能越界。不能用于添加元素,添加应该用push_back方法

解释为什么可以列表初始化?

这是C++11最新的特性,

//以前初始化
vector<int> in;
in.pushback(1);
in.pushback(2);
in.pushback(3);

//现在
vector<int> in = {1 , 2 , 3};//3个元素列表初始化
vector<int> in{1 , 2 , 3};//3个元素列表初始化
//这两种形式是等效的。

C++11允许构造函数和其他函数把初始化列表(通过关键字initializer_list传参)当做参数使用。这时候,只要定义对象的时候,初始化通过{},那么就会调用含有initializer_list关键字的构造函数。这就是为什么支持初始化列表。我们需要些支持初始化列表的类,只需要写一个对应的构造函数即可。

#include 
#include 
using namespace std;

class Mynumber{
public:
    Mynumber(initializer_list<int> ii){
        for(auto i = ii.begin() ; i != ii.end() ; i++)
            mynumber.push_back(*i);
    }
    Mynumber(int i){
        mynumber.push_back(i);
    }

    void Myprint(){
        for(int i = 0 ; i < mynumber.size() ; i++)
            cout << mynumber[i] << " ";
        cout << endl;
    }

private:
    vector<int> mynumber;
};
int main(void)
{
    Mynumber a0{1,2,3,4,5,6};//调用第一个列表初始化构造函数
    Mynumber a1(1);//调用第二个构造函数
    a0.Myprint();//1 2 3 4 5 6
    a1.Myprint();//1
}

迭代器介绍

begin负责返回指向第一个元素。
end成员负责返回尾元素的下一个位置,返回的迭代器称为尾后迭代器。
如果容器为空,那么begin和end返回同一个迭代器,也是尾后迭代器。

数组

这里内容和c语言里面就是一样的。但是有几种理解复杂数组的方法必须始终明白清楚。以前在c语言书籍里面没看清楚。

c语言运算符对应优先级:
C++ Primer 3章 字符串、向量和数组_第1张图片

  • 结合性只有在同优先级运算符下才生效。
  • 关系运算符优先级高于逻辑运算符。
//理解复杂数组声明
//方法:运算符优先级+从从内到外,从右到左依次结合理解。
//理解数组声明,最好就是从数组名字开始从内向外顺序阅读。
int *ptrs[10];//首先定义大小为10的数组,名字是ptrs,然后指向int *的指针,因此称之为指针数组。
int (*ptr)[10];//首先括号里面ptr是一个指针,然后右边既指针指向维度10的数组,然后左边,明白数组里面是int类型。ptr是指向数组(存放int 维度10)的指针。
int main(void)
{
    char *ptr[] = {"Hello" , "World" ,"c++"};//指针数组
    char **beg = begin(ptr);//注意只用begin的时候,前面变量和后面变量类型应该匹配,ptr[1]里面存放的是指针,那么ptr实际上等效于指向指针的指针,因此双重效果。
    char **last = end(ptr);//这里c语言基础知识扎实才可以理解。
    while(beg != last){
        cout << *beg << endl;
        beg++;
    }
    exit(0);
}
  • c++11标准引入了两个名为begin和end的函数。begin返回数组首元素的指针,end返回尾部元素下一个位置的指针,因此可以利用 !=来进行判断,和迭代器功能类似,但是end返回的指针不能解引用和递增操作,仅仅适合判断。
  • 注意此处指针的指针的使用方法。
  • 数组访问如a[1]等效于(a+1),仅仅为了方便,才弄成a[1]的形式而已计算机处理的是(a+1)。
  • 因为有上面那条结论,所以p[-1],p[-2]这种形式可能是合法的,只要没有越过数组前后界限。

c库字符串函数和c++string对象的区别:

多维数组

未完待续……

你可能感兴趣的:(C,PlusPlus,string,对象,c语言,c++)