下面的代码来自c++ primer plus第5版第12章,书中代码写的非常好:

// string1.h -- fixed and augmented string class definition
#include <iostream>
using std::ostream;
using std::istream;

#ifndef STRING1_H_
#define STRING1_H_
class String
{
private:
    char * str;             // pointer to string
    int len;                // length of string
    static int num_strings; // number of objects
    static const int CINLIM = 80;  // cin input limit
public:
// constructors and other methods
    String(const char * s); // constructor
    String();               // default constructor
    String(const String &); // copy constructor
    ~String();              // destructor
    int length () const { return len; }
// overloaded operator methods    
    String & operator=(const String &);
    String & operator=(const char *);
    char & operator[](int i);
    const char & operator[](int i) const; 
// overloaded operator friends
    friend bool operator<(const String &st, const String &st2);
    friend bool operator>(const String &st1, const String &st2);
    friend bool operator==(const String &st, const String &st2);
    friend ostream & operator<<(ostream & os, const String & st);
    friend istream & operator>>(istream & is, String & st);
// static function
    static int HowMany();
};
#endif
num_strings 主要是为了统计对象创建的个数.
cpp文件:
// string1.cpp -- String class methods
#include <cstring>                 // string.h for some
#include "string1.h"               // includes <iostream>
using std::cin;
using std::cout;

// initializing static class member

int String::num_strings = 0;

// static method
int String::HowMany()
{
    return num_strings;
}

// class methods
String::String(const char * s)     // construct String from C string
{
    len = std::strlen(s);          // set size
    str = new char[len + 1];       // allot storage
    std::strcpy(str, s);           // initialize pointer
    num_strings++;                 // set object count
}

String::String()                   // default constructor
{
    len = 4;
    str = new char[1];
    str[0] = '\0';                 // default string
    num_strings++;
}

String::String(const String & st)
{
    num_strings++;             // handle static member update
    len = st.len;              // same length
    str = new char [len + 1];  // allot space
    std::strcpy(str, st.str);  // copy string to new location
}

String::~String()                     // necessary destructor
{
    --num_strings;                    // required
    delete [] str;                    // required
}

// overloaded operator methods    

    // assign a String to a String
String & String::operator=(const String & st)
{
    if (this == &st)
        return *this;
    delete [] str;
    len = st.len;
    str = new char[len + 1];
    std::strcpy(str, st.str);
    return *this;
}

    // assign a C string to a String
String & String::operator=(const char * s)
{
    delete [] str;
    len = std::strlen(s);
    str = new char[len + 1];
    std::strcpy(str, s);
    return *this;
}

    // read-write char access for non-const String
char & String::operator[](int i)
{
    return str[i];
}

    // read-only char access for const String
const char & String::operator[](int i) const
{
    return str[i];
}

// overloaded operator friends

bool operator<(const String &st1, const String &st2)
{
    return (std::strcmp(st1.str, st2.str) < 0);
}

bool operator>(const String &st1, const String &st2)
{
    return st2.str < st1.str;
}

bool operator==(const String &st1, const String &st2)
{
    return (std::strcmp(st1.str, st2.str) == 0);
}

    // simple String output
ostream & operator<<(ostream & os, const String & st)
{
    os << st.str;
    return os; 
}

    // quick and dirty String input
istream & operator>>(istream & is, String & st)
{
    char temp[String::CINLIM];
    is.get(temp, String::CINLIM);
    if (is)
        st = temp;
    while (is && is.get() != '\n') //  丢弃多余的字符
        continue;
    return is; 
}

代码有许多值得注意的地方:

1.为什么构造函数里面是

str = new char[1];
str[0] = '\0';
而不是
str=new char;
上面2中方式分配的内存量相同,区别在于前者与析构函数兼容,而后者不兼容。析构函数含有如下代码:
delete[] str;
delete[] 与使用new【】初始化的指针和空指针都兼容,因此对于下面的代码:
str = new char[1];
str[0] = '\0';
可修改为
str=0;//set str to the null pointer

对于以其他方式初始化的指针,使用delete[] 删除,结果是不确定的:

char words[15]="ab";

char *p=words;

char *p2=new char;

char *p3;

delete[] p1; //undefined,so don't do it //会产生run time error

delete[] p2;//undefined,so don't do it

delete[] p3;//undefined,so don't do it 会产生run time error.

上面几种方式都不正确。

2.为什么我们要2次重载:

// read-write char access for non-const String
char & String::operator[](int i)
{
return str[i];
}

// read-only char access for const String
const char & String::operator[](int i) const
{
return str[i];
}

是因为如果没有重载常量的[],下面的代码将出错:

const String s("hello");

cout<<a[1];

元素是s是常量,而上述方法无法确保不修改数据。

所以在重载时,c++将区分常量和非常量函数的特征标

有了上述的重载后,

const string s("helloo");

cin>>s[1];就会报错。

istream的get函数有许多重载版本:

single character (1)
  int get();
  istream& get (char& c);
c-string (2)
  istream& get (char* s, streamsize n);
  istream& get (char* s, streamsize n, char delim);
stream buffer (3)
  istream& get (streambuf& sb);
  istream& get (streambuf& sb, char delim);

cin.get()读取单个字符并返回,http://www.cplusplus.com/reference/istream/istream/get/

可不可以将构造函数换成:

String()

{

 str="default string";

 len=strlen(str);

}

答:不可以,没有使用new[]来初始化str,对默认对象调用析构函数时,使用delete【】来释放,对不是使用new初始化的指针使用

delete,其结果将是不确定的,并可能是有害的。

在构造函数中使用new应注意的问题

使用new来初始化对象的指针成员时必须要特别小心,具体地说,应该是这样:

1.如果在构造函数中使用new来初始化成员指针,则应在析构中调用delete

2,new和delete必须相互兼容,new对于delete,new[]对于delete[]。

3.如果有多个构造函数,则必须以相同的方式使用new,要么带中括号,要么都不带。因为只有一个析构函数,因此所有的构造函数都必须与他兼容。不过,可以在一个构造函数中使用new来初始化指针,而在另一个构造函数中将指针初始化为空(null或0,这是

因为delete(无论是带【】还是不带【】)都可以用于空指针

 

ostream & operator<<(ostream & os, const String & st) 
可以可以不返回引用?
答:绝对不可以
cout一般都可以链式操作cout<<s<<s2;
如果不返回引用,如果返回类型为ostream,将要求ostream类的复制构造函数,而ostream没有任何公有的复制构造函数。幸运的是,
返回一个指向cout的引用不会带来任何问题。
如果不写成引用,编译不通过。报错:
无法访问 private 成员(在“std::basic_ios<_Elem,_Traits>”类中声明)
我们在xiobase文件中科院看到:
private:
ios_base& __CLR_OR_THIS_CALL operator=(const ios_base& _Right)
 private: ios_base(const ios_base&);
显然,拷贝构造函数与复制操作符都声明为私有。 因此,重载操作符应该如下: ostream& operator<<(ostream &out , CA &ca)

istream也是如此。

测试:

这是书本的测试:

STL string 模拟_#includeSTL string 模拟_初始化_02
// sayings1.cpp -- using expanded String class
// compile with string1.cpp
#include <iostream>
#include "string1.h" 
const int ArSize = 10;
const int MaxLen =81;
int main()
{
    using std::cout;
    using std::cin;
    using std::endl;
    String name;
    cout <<"Hi, what's your name?\n>> ";
    cin >> name;

    cout << name << ", please enter up to " << ArSize
         << " short sayings <empty line to quit>:\n";
    String sayings[ArSize];     // array of objects
    char temp[MaxLen];          // temporary string storage
    int i;
    for (i = 0; i < ArSize; i++)
    {
        cout << i+1 << ": ";
        cin.get(temp, MaxLen);
        while (cin && cin.get() != '\n')
            continue;
        if (!cin || temp[0] == '\0')    // empty line?
            break;              // i not incremented
        else
            sayings[i] = temp;  // overloaded assignment
    }
    int total = i;              // total # of lines read

    cout << "Here are your sayings:\n";
    for (i = 0; i < total; i++)
        cout << sayings[i][0] << ": " << sayings[i] << endl;

    int shortest = 0;
    int first = 0;
    for (i = 1; i < total; i++)
    {
        if (sayings[i].length() < sayings[shortest].length())
            shortest = i;
        if (sayings[i] < sayings[first])
            first = i;
    }
    cout << "Shortest saying:\n" << sayings[shortest] << endl;;
    cout << "First alphabetically:\n" << sayings[first] << endl;
    cout << "This program used "<< String::HowMany() 
         << " String objects. Bye.\n";

    return 0; 
}
View Code

这是我的测试;

#include"String.h"
#include<cassert>
#include<string>


int main()
{
 
    
    //copy constrtor 
    String s1("abc");
    cout<<s1<<endl;

    String s2(s1);
    cout<<s2<<endl;

    // operator=(const char* s);
    s1="helloworld";
    cout<<s1<<endl;
    // operator=(const String & other);
    String s3("youcan");
    s1=s3;
    cout<<s1<<endl;
    
    //调用赋值构造函数
    String s4="dddd";
    cout<<s4<<endl;
    cout<<s1.howMany()<<endl;//输出4

    String s5="helloworld";
    cout<<s5[0]<<endl;
    s5[0]='z';
    cout<<s5<<endl;
    const String s6("const");
    cout<<s6[0]<<endl;
 
}

上面的全都来自c++ primer plus这本书。