zoukankan      html  css  js  c++  java
  • Cpp Chapter 16: The string Class and the Standard Template Library Part1

    (这已经是第二次博客园吞我东西了,有点心态爆炸)

    16.1 The string Class

    ) Constructing a string
    Here is a table which shows the seven form of constructors that the string class has:

    Constructor Description
    string(const char * s) initializes a string object to the C-style string pointed to by s
    string(size_type n, char c) initializes a string object of n elements, each initialized to c
    string(const string & str) initializes a string object to the string object str(copy constructor)
    string() creates a default string object of size 0
    string(const char * s, size_type n) initializes a string object to the C-style string pointed to by s, continuing for n characters even it exceeds s
    template string(Iter begin, Iter end) initializes a string to the values in the range [begin, end)
    string(const string & str, size_type pos, size_type n = npos) initializes a string object to str, starting from pos and going to end or terminate with n characters

    ) The string class input
    Typically, you have two ways of string input:
    1

    string stuff;
    cin >> stuff;
    

    In this way you read a word.
    2

    string stuff;
    getline(cin, stuff);
    

    In this way you read a line of characters.
    An important feature of string class is that it automatically size the string object to hold the input characters.
    The getline() function for the string class reads characters from input unless:
    1 the end-of-file is encountered
    2 the delimiting character( ) is reached
    3 the maximum possible number of characters is read, which is represented by string::npos
    The operator>>() function reads up to the whitespace, which is typically ' ' and any characters that makes isspace() return true

    Next comes a program that illustrates the features of file output:

    // strfile.cpp -- read strings from a file
    #include <iostream>
    #include <string>
    #include <cstdlib>
    #include <fstream>
    
    int main()
    {
        using namespace std;
        ifstream fin;
        fin.open("tobuy.txt");
        if (fin.is_open() == false)
        {
            cerr << "Can't open file. Bye.
    ";
            exit(EXIT_FAILURE);
        }
        string item;
        int count = 0;
        getline(fin, item, ':');
        while (fin)
        {
            ++count;
            cout << count << ": " << item << endl;
            getline(fin, item, ':');
        }
        cout << "Done
    ";
        fin.close();
        return 0;
    }
    

    Noteworthy:
    1 Typically, the file you tried to read should be in the same directory with the program. If not so, you can provide a full path name:

    fin.open("C:\CPP\tobuy.txt");
    

    The \ here is an escape sequence, which represents one ''
    2 You open a file with fin.open(path), and fin is an ifstream object. To get contents of the file, you use getline(fin, str, ':') where ':' stands for the delimiter here, which means that the input terminates when it reaches ':'
    3 while(fin) loops as long as the input is not EOF and is good.

    ) Working with strings
    1 You can compare strings with the overloaded relational operators, with one object being considered less if it occurs earlier in the machine collating sequence(ASCII).
    2 You can determine the size of a string by length() or size():

    if (snake1.length() == snake2.size())
        cout << "Both strings have the same length";
    

    3 You can search for a substring or a character in a string"
    size_type find(const string & str, size_type pos = 0) const finds the first occurence of str, starting from pos, and returning the position found or pos otherwise.
    Next comes a program using the features of the string class, which is a game that lets you guess a word with a given length:

    // hangman.cpp -- some string methods
    #include <iostream>
    #include <string>
    #include <cstdlib>
    #include <ctime>
    #include <cctype>
    using std::string;
    const int NUM = 26;
    const string wordlist[NUM] = {"apiary", "beetle", "cereal", "danger", "ensign", "florid", "garage", "health", "insult",
        "jackal", "keeper", "loaner", "manage", "nonce", "onset", "plaid", "quilt", "remote", "stolid", "train", "useful", "valid", "whence", "exnon", "yearn", "zipppy"};
    
    int main()
    {
        using std::cout;
        using std::cin;
        using std::tolower;
        using std::endl;
        std::srand(std::time(0));
        char play;
        cout << "Will you play a word game? <y/n>? ";
        cin >> play;
        play = tolower(play);
        while (play == 'y')
        {
            string target = wordlist[std::rand() % NUM];
            int length = target.length();
            string attempt(length, '-');
            string badchars;
            int guesses = 6;
            cout << "Guess my secret word. It has " << length << " letters, and you guess
    one letter at a time. You get " << guesses << " wrong guesses.
    ";
            cout << "Your word: " << attempt << endl;
            while (guesses > 0 && attempt != target)
            {
                char letter;
                cout << "Guess a letter: ";
                cin >> letter;
                if (badchars.find(letter) != string::npos || attempt.find(letter) != string::npos)
                {
                    cout << "You already guessed that. Try again.
    ";
                    continue;
                }
                int loc = target.find(letter);
                if (loc == string::npos)
                {
                    cout << "Oh, bad guess!
    ";
                    --guesses;
                    badchars += letter;
                }
                else
                {
                    cout << "Good guess!
    ";
                    attempt[loc] = letter;
                    loc = target.find(letter, loc + 1); // start searching from loc+1
                    while (loc != string::npos)
                    {
                        attempt[loc] = letter;
                        loc = target.find(letter, loc + 1);
                    };
                }
                cout << "Your word: " << attempt << endl;
                if (attempt != target)
                {
                    if (badchars.length() > 0)
                        cout << "Bad choices: " << badchars << endl;
                    cout << guesses << " bad guesses left
    ";
                }
            }
            if (guesses > 0)
                cout << "That's right!
    ";
            else
                cout << "Sorry, the word is " << target << ".
    ";
            cout << "Will you play another? <y/n> ";
            cin >> play;
            play = tolower(play);
        }
    
        cout << "Bye
    ";
        return 0;
    }
    

    Noteworthy:
    1 Recall that string.find(letter) returns string::npos if it doesn't find the required letter in it. So the program uses this sentence to test if the user's input is already guessed before:

    if (badchars.find(letter) != string::npos || attempt.find(letter) != string::npos)
    

    npos is a static member of string class, it marks the greatest amount of characters that a string could have, thus used in find() to indicate that the letter required isn't found in the string.
    2 The program also used + for string concatenation, > for comparison of string and the features provided by string class.

    ) What else does the string class offer
    The string class objects will automatically adjust its size, so it initially will allocate a larger memory for a string object, letting it to grow in size. When it grows to exceed the memory, the compiler allocates a block of memory that is twice the size of the previous one and copy the object contents to the new place.
    The capacity() method returns the size of the current block, and the reserve() method allows you to request a minimum size for the block.
    Here is a program illustrating the mentioned features about memory allocation:

    // str2.cpp -- capacity() and reserve()
    #include <iostream>
    #include <string>
    int main()
    {
        using namespace std;
        string empty;
        string small = "bit";
        string larger = "Elephants are a girl's best friend";
        cout << "Sizes
    ";
        cout << "	empty: " << empty.size() << endl;
        cout << "	small: " << small.size() << endl;
        cout << "	larger: " << larger.size() << endl;
        cout << "Capacities:
    ";
        cout << "	empty: " << empty.capacity() << endl;
        cout << "	small: " << small.capacity() << endl;
        cout << "	larger: " << larger.capacity() << endl;
        empty.reserve(50);
        cout << "Capacity after empty.reserve(50): " << empty.capacity() << endl;
        return 0;
    }
    

    ) Conversion between C-style string and string objects
    In order to convert from C-style string to string objects, use the string constructor:

    char a[2] = "ab";
    string b = string(a);
    

    In order to convert from string objects to C-style strings(e.g., opening file), use string.c_str():

    string b = "haha.txt";
    fout.open(b.c_str());
    

    16.2 Smart Pointer Template Classes

    ) Why smart pointer?
    Sometimes you might forget to append delete ptr after you declare a block of memory which is pointed to by ptr, or a function would terminate by throwing exception and skip the delete code. In both cases, it causes a memory leak. In order to solve this problem one for all, you may design a pointer which has a destructor that automatically free the memory it points to. That is just the idea of smart pointers, to be more specific, auto_ptr, unique_ptr and shared_ptr.

    ) Using smart pointers
    In order to use smart pointers, you include memory header file and use the syntax of ordinary templates:

    auto_ptr<double> pd(new double);
    auto_ptr<string> pd(new string);
    

    Note that smart pointers belong to the std namespace. Next comes code that uses smart pointers:

    // smrtptrs.cpp -- using three kinds of smart pointers
    // requires support of C++11 shared_ptr and unique_ptr
    #include <iostream>
    #include <string>
    #include <memory>
    
    class Report
    {
    private:
        std::string str;
    public:
        Report(const std::string s) : str(s) {std::cout << "Object created!
    ";}
        ~Report() {std::cout << "Object deleted!
    ";}
        void comment() const {std::cout << str << "
    ";}
    };
    
    int main()
    {
        {
            std::auto_ptr<Report> ps (new Report("using auto_ptr"));
            ps->comment();
        }
        {
            std::shared_ptr<Report> ps (new Report("using shared_ptr"));
            ps->comment();
        }
        {
            std::unique_ptr<Report> ps (new Report("using unique_ptr"));
            ps->comment();
        }
        return 0;
    }
    

    Noteworthy:
    1 You could just use smart pointer as ordinary pointers. It supports derefrencing*, call members'->', and assign to other pointers.
    2 Avoid letting smart pointers point to non-heap memory:

    string vacation("oh");
    shared_ptr<string> pvac(vacation);
    

    When pvac expires, it would delete vacation, which is not right.

    ) Smart pointer consideration
    Consider:

    vocation = ps;
    

    When you let two smart pointers point to a same chunk of memory, it will free it twice when they both expired, which would raise an error. There are strategies that solve this:
    1 Design the assignment to do deep copy
    2 Take the concept of ownership, meaning that only the pointer that owns the memory could delete it with its destructor. This is the strategy used for auto_ptr and unique_ptr.
    3 Create an even smarter pointer that keeps track of how many pointer point to a particular object. This is called reference counting. shared_ptr uses this strategy.
    Next comes an example that auto_ptr works poorly:

    // fowl.cpp -- auto_ptr a poor choice
    #include <iostream>
    #include <string>
    #include <memory>
    
    int main()
    {
        using namespace std;
        auto_ptr<string> films[5] = {auto_ptr<string> (new string("A")),
                                    auto_ptr<string> (new string("B")),
                                    auto_ptr<string> (new string("C")),
                                    auto_ptr<string> (new string("D")),
                                    auto_ptr<string> (new string("E"))};
        auto_ptr<string> pwin;
        pwin = films[2]; // films[2] loses ownership
    
        cout << "The nominees for best avian baseball film are
    ";
        for (int i = 0; i < 5; i++)
            cout << *films[i] << endl;
        cout << "The winner is " << *pwin << "!
    ";
        cin.get();
        return 0;
    }
    

    This program will crash due to the sentence:

    pwin = films[2];
    

    As auto_ptr incorporates the idea of ownership, this assignment passes ownership from films[2] to pwin. After an auto_ptr gives up the ownership of an object, it no longer provides access to it. So when you later do cout << *films[2]; it will find a null-pointer and leads to a crash.
    But if you use shared_ptr here, the program runs properly because both pointers retain access to the object.

    ---恢复内容结束---

    ## 16.1 The string Class
    ) Constructing a string
    Here is a table which shows the seven form of constructors that the string class has:

    Constructor Description
    string(const char * s) initializes a string object to the C-style string pointed to by s
    string(size_type n, char c) initializes a string object of n elements, each initialized to c
    string(const string & str) initializes a string object to the string object str(copy constructor)
    string() creates a default string object of size 0
    string(const char * s, size_type n) initializes a string object to the C-style string pointed to by s, continuing for n characters even it exceeds s
    template string(Iter begin, Iter end) initializes a string to the values in the range [begin, end)
    string(const string & str, size_type pos, size_type n = npos) initializes a string object to str, starting from pos and going to end or terminate with n characters

    ) The string class input
    Typically, you have two ways of string input:
    1

    string stuff;
    cin >> stuff;
    

    In this way you read a word.
    2

    string stuff;
    getline(cin, stuff);
    

    In this way you read a line of characters.
    An important feature of string class is that it automatically size the string object to hold the input characters.
    The getline() function for the string class reads characters from input unless:
    1 the end-of-file is encountered
    2 the delimiting character( ) is reached
    3 the maximum possible number of characters is read, which is represented by string::npos
    The operator>>() function reads up to the whitespace, which is typically ' ' and any characters that makes isspace() return true

    Next comes a program that illustrates the features of file output:

    // strfile.cpp -- read strings from a file
    #include <iostream>
    #include <string>
    #include <cstdlib>
    #include <fstream>
    
    int main()
    {
        using namespace std;
        ifstream fin;
        fin.open("tobuy.txt");
        if (fin.is_open() == false)
        {
            cerr << "Can't open file. Bye.
    ";
            exit(EXIT_FAILURE);
        }
        string item;
        int count = 0;
        getline(fin, item, ':');
        while (fin)
        {
            ++count;
            cout << count << ": " << item << endl;
            getline(fin, item, ':');
        }
        cout << "Done
    ";
        fin.close();
        return 0;
    }
    

    Noteworthy:
    1 Typically, the file you tried to read should be in the same directory with the program. If not so, you can provide a full path name:

    fin.open("C:\CPP\tobuy.txt");
    

    The \ here is an escape sequence, which represents one ''
    2 You open a file with fin.open(path), and fin is an ifstream object. To get contents of the file, you use getline(fin, str, ':') where ':' stands for the delimiter here, which means that the input terminates when it reaches ':'
    3 while(fin) loops as long as the input is not EOF and is good.

    ) Working with strings
    1 You can compare strings with the overloaded relational operators, with one object being considered less if it occurs earlier in the machine collating sequence(ASCII).
    2 You can determine the size of a string by length() or size():

    if (snake1.length() == snake2.size())
        cout << "Both strings have the same length";
    

    3 You can search for a substring or a character in a string"
    size_type find(const string & str, size_type pos = 0) const finds the first occurence of str, starting from pos, and returning the position found or pos otherwise.
    Next comes a program using the features of the string class, which is a game that lets you guess a word with a given length:

    // hangman.cpp -- some string methods
    #include <iostream>
    #include <string>
    #include <cstdlib>
    #include <ctime>
    #include <cctype>
    using std::string;
    const int NUM = 26;
    const string wordlist[NUM] = {"apiary", "beetle", "cereal", "danger", "ensign", "florid", "garage", "health", "insult",
        "jackal", "keeper", "loaner", "manage", "nonce", "onset", "plaid", "quilt", "remote", "stolid", "train", "useful", "valid", "whence", "exnon", "yearn", "zipppy"};
    
    int main()
    {
        using std::cout;
        using std::cin;
        using std::tolower;
        using std::endl;
        std::srand(std::time(0));
        char play;
        cout << "Will you play a word game? <y/n>? ";
        cin >> play;
        play = tolower(play);
        while (play == 'y')
        {
            string target = wordlist[std::rand() % NUM];
            int length = target.length();
            string attempt(length, '-');
            string badchars;
            int guesses = 6;
            cout << "Guess my secret word. It has " << length << " letters, and you guess
    one letter at a time. You get " << guesses << " wrong guesses.
    ";
            cout << "Your word: " << attempt << endl;
            while (guesses > 0 && attempt != target)
            {
                char letter;
                cout << "Guess a letter: ";
                cin >> letter;
                if (badchars.find(letter) != string::npos || attempt.find(letter) != string::npos)
                {
                    cout << "You already guessed that. Try again.
    ";
                    continue;
                }
                int loc = target.find(letter);
                if (loc == string::npos)
                {
                    cout << "Oh, bad guess!
    ";
                    --guesses;
                    badchars += letter;
                }
                else
                {
                    cout << "Good guess!
    ";
                    attempt[loc] = letter;
                    loc = target.find(letter, loc + 1); // start searching from loc+1
                    while (loc != string::npos)
                    {
                        attempt[loc] = letter;
                        loc = target.find(letter, loc + 1);
                    };
                }
                cout << "Your word: " << attempt << endl;
                if (attempt != target)
                {
                    if (badchars.length() > 0)
                        cout << "Bad choices: " << badchars << endl;
                    cout << guesses << " bad guesses left
    ";
                }
            }
            if (guesses > 0)
                cout << "That's right!
    ";
            else
                cout << "Sorry, the word is " << target << ".
    ";
            cout << "Will you play another? <y/n> ";
            cin >> play;
            play = tolower(play);
        }
    
        cout << "Bye
    ";
        return 0;
    }
    

    Noteworthy:
    1 Recall that string.find(letter) returns string::npos if it doesn't find the required letter in it. So the program uses this sentence to test if the user's input is already guessed before:

    if (badchars.find(letter) != string::npos || attempt.find(letter) != string::npos)
    

    npos is a static member of string class, it marks the greatest amount of characters that a string could have, thus used in find() to indicate that the letter required isn't found in the string.
    2 The program also used + for string concatenation, > for comparison of string and the features provided by string class.

    ) What else does the string class offer
    The string class objects will automatically adjust its size, so it initially will allocate a larger memory for a string object, letting it to grow in size. When it grows to exceed the memory, the compiler allocates a block of memory that is twice the size of the previous one and copy the object contents to the new place.
    The capacity() method returns the size of the current block, and the reserve() method allows you to request a minimum size for the block.
    Here is a program illustrating the mentioned features about memory allocation:

    // str2.cpp -- capacity() and reserve()
    #include <iostream>
    #include <string>
    int main()
    {
        using namespace std;
        string empty;
        string small = "bit";
        string larger = "Elephants are a girl's best friend";
        cout << "Sizes
    ";
        cout << "	empty: " << empty.size() << endl;
        cout << "	small: " << small.size() << endl;
        cout << "	larger: " << larger.size() << endl;
        cout << "Capacities:
    ";
        cout << "	empty: " << empty.capacity() << endl;
        cout << "	small: " << small.capacity() << endl;
        cout << "	larger: " << larger.capacity() << endl;
        empty.reserve(50);
        cout << "Capacity after empty.reserve(50): " << empty.capacity() << endl;
        return 0;
    }
    

    ) Conversion between C-style string and string objects
    In order to convert from C-style string to string objects, use the string constructor:

    char a[2] = "ab";
    string b = string(a);
    

    In order to convert from string objects to C-style strings(e.g., opening file), use string.c_str():

    string b = "haha.txt";
    fout.open(b.c_str());
    

    16.2 Smart Pointer Template Classes

    ) Why smart pointer?
    Sometimes you might forget to append delete ptr after you declare a block of memory which is pointed to by ptr, or a function would terminate by throwing exception and skip the delete code. In both cases, it causes a memory leak. In order to solve this problem one for all, you may design a pointer which has a destructor that automatically free the memory it points to. That is just the idea of smart pointers, to be more specific, auto_ptr, unique_ptr and shared_ptr.

    ) Using smart pointers
    In order to use smart pointers, you include memory header file and use the syntax of ordinary templates:

    auto_ptr<double> pd(new double);
    auto_ptr<string> pd(new string);
    

    Note that smart pointers belong to the std namespace. Next comes code that uses smart pointers:

    // smrtptrs.cpp -- using three kinds of smart pointers
    // requires support of C++11 shared_ptr and unique_ptr
    #include <iostream>
    #include <string>
    #include <memory>
    
    class Report
    {
    private:
        std::string str;
    public:
        Report(const std::string s) : str(s) {std::cout << "Object created!
    ";}
        ~Report() {std::cout << "Object deleted!
    ";}
        void comment() const {std::cout << str << "
    ";}
    };
    
    int main()
    {
        {
            std::auto_ptr<Report> ps (new Report("using auto_ptr"));
            ps->comment();
        }
        {
            std::shared_ptr<Report> ps (new Report("using shared_ptr"));
            ps->comment();
        }
        {
            std::unique_ptr<Report> ps (new Report("using unique_ptr"));
            ps->comment();
        }
        return 0;
    }
    

    Noteworthy:
    1 You could just use smart pointer as ordinary pointers. It supports derefrencing*, call members'->', and assign to other pointers.
    2 Avoid letting smart pointers point to non-heap memory:

    string vacation("oh");
    shared_ptr<string> pvac(vacation);
    

    When pvac expires, it would delete vacation, which is not right.

    ) Smart pointer consideration
    Consider:

    vocation = ps;
    

    When you let two smart pointers point to a same chunk of memory, it will free it twice when they both expired, which would raise an error. There are strategies that solve this:
    1 Design the assignment to do deep copy
    2 Take the concept of ownership, meaning that only the pointer that owns the memory could delete it with its destructor. This is the strategy used for auto_ptr and unique_ptr.
    3 Create an even smarter pointer that keeps track of how many pointer point to a particular object. This is called reference counting. shared_ptr uses this strategy.
    Next comes an example that auto_ptr works poorly:

    // fowl.cpp -- auto_ptr a poor choice
    #include <iostream>
    #include <string>
    #include <memory>
    
    int main()
    {
        using namespace std;
        auto_ptr<string> films[5] = {auto_ptr<string> (new string("A")),
                                    auto_ptr<string> (new string("B")),
                                    auto_ptr<string> (new string("C")),
                                    auto_ptr<string> (new string("D")),
                                    auto_ptr<string> (new string("E"))};
        auto_ptr<string> pwin;
        pwin = films[2]; // films[2] loses ownership
    
        cout << "The nominees for best avian baseball film are
    ";
        for (int i = 0; i < 5; i++)
            cout << *films[i] << endl;
        cout << "The winner is " << *pwin << "!
    ";
        cin.get();
        return 0;
    }
    

    This program will crash due to the sentence:

    pwin = films[2];
    

    As auto_ptr incorporates the idea of ownership, this assignment passes ownership from films[2] to pwin. After an auto_ptr gives up the ownership of an object, it no longer provides access to it. So when you later do cout << *films[2]; it will find a null-pointer and leads to a crash.
    But if you use shared_ptr here, the program runs properly because both pointers retain access to the object.

  • 相关阅读:
    go语言:go写的日记簿(开源)
    64位pyqt打包生成的python64位的版本,在win32位下无效
    分享:Notepad++ 6.2.3 发布,开源文本编辑器
    PyQt v4 Python Bindings for Qt v4
    python基于http协议编程:httplib,urllib和urllib2
    XP下IIS5运行MVC站点
    你必须要知道的架构知识~第六章 .NET原生态的ORM软件对比
    从微软的DBML文件中我们能学到什么(它告诉了我们什么是微软的重中之重)~七 为DBContext对象应该作为其它实体操作类型的基类,并为它提供统一的提交动作
    MVC最为标准的添加操作(从实体验证到实体入库)
    MVC命名空间中的~UrlHelper中的Action方法告诉我们方法重载的重要性(路由的统一)
  • 原文地址:https://www.cnblogs.com/fsbblogs/p/9986964.html
Copyright © 2011-2022 走看看