·## 改进后的StringBad类:
//stringbad.h
#include <iostream>
#ifndef STRINGBAD_H_
#define STRINGBAD_H_
class StringBad
{
private :
char *str;
int len;
static int num_strings;
static const int CINLIM = 80;//cin的长度限制
public:
//构造函数和其他方法
StringBad();
StringBad(const char * s);
StringBad(const StringBad &s);
~StringBad();
int length()const { return len; }
//重载方法
StringBad& operator=(const StringBad&s);
StringBad& operator=(const char*s);
char& operator[](int i);
const char& operator[](int i)const;
//友元重载方法
friend bool operator<(const StringBad &s1, const StringBad &s2);
friend bool operator>(const StringBad &s1, const StringBad &s2);
friend bool operator==(const StringBad &s1, const StringBad &s2);
friend std::istream &operator>>(std::istream &is, StringBad &st);
friend std::ostream &operator<<(std::ostream &os, const StringBad &st);
//静态成员方法
static int HowMany();
};
#endif
//stringbad.cpp
#include "stringbad.h"
//初始化静态成员变量
int StringBad::num_strings = 0;
StringBad::StringBad()
{
len = 0;
str = new char[1];
str[0] = '\0';
num_strings++;
}
StringBad::StringBad(const char * s)
{
len = std::strlen(s);
str = new char[len + 1];
std::strcpy(str, s);
num_strings++;
}
StringBad::StringBad(const StringBad & s)
{
num_strings++;
len = s.len ;
str = new char[len + 1];
std::strcpy(str, s.str);
}
StringBad::~StringBad()
{
num_strings--;
if (NULL != str)
{
delete[]str;
}
}
StringBad & StringBad::operator=(const StringBad & s)
{
if (this == &s)
return *this;
delete[]str;//清空原来的字符串
len = s.len;
str = new char[len + 1];
std::strcpy(str, s.str);
return *this;
}
StringBad & StringBad::operator=(const char * s)
{
delete[]str;//清空原来的字符串
len = std::strlen(s);
str = new char[len + 1];
std::strcpy(str, s);
return *this;
}
//适用于 StringBad 的[]
char & StringBad::operator[](int i)
{
if (i < len)
return str[i];
else
return str[len];
}
//适用于 const StringBad[]
const char & StringBad::operator[](int i) const
{
if (i < len)
return str[i];
else
return str[len];
}
bool operator<(const StringBad & s1, const StringBad & s2)
{
return (std::strcmp(s1.str, s2.str) < 0);
}
bool operator>(const StringBad & s1, const StringBad & s2)
{
return s2<s1;
}
bool operator==(const StringBad & s1, const StringBad & s2)
{
return (std::strcmp(s1.str, s2.str) == 0);
}
std::istream & operator >> (std::istream & is,StringBad & st)
{
char temp[StringBad::CINLIM];
is.get(temp, StringBad::CINLIM);
if (is)
st = temp;
while (is&&is.get() != '\n')
continue;
return is;
}
std::ostream & operator<<(std::ostream & os, const StringBad & st)
{
os << st.str;
return os;
}
//静态成员函数
int StringBad::HowMany()
{
return num_strings;
}
//main.cpp
#include"stringbad.h"
using namespace std;
void main()
{
StringBad phl = "uuuu";
StringBad lll = "uuuu";
cout << phl.length() << endl;
cout << phl[1] << endl;
cout << (phl > lll) << endl;
cout << (phl < lll) << endl;
cout << (phl == lll) << endl;
cin >> phl;
cout << phl << endl;
cout<<("uuuu" == phl) << endl;
int count = StringBad::HowMany();
cout << count << endl;
}
静态成员函数:
-
声明是必须包含关键字static,函数定义不能包含关键字static。
-
不能通过对象调用静态成员函数,如果静态成员成员函数是在公有部分声明的,则可以使用类名和作用域解析符来调用。
int count = StringBad::HowMany(); -
静态成员函数甚至不能使用this指针。由于静态成员函数不和特定的对象相关联,因此只能使用静态数据成员,即只能访问静态成员变量。
在构造函数中使用new的注意事项:
- 在构造函数中使用new初始化对象的指针成员变量,则应该在析构函数中使用delete
- new和delete必须兼容,new对应delete,new[]对应delete[]
- 如果有多个构造函数,则必须使用相同方式的new,要么带中括号,要么不带。因为只有一个析构函数,所有的构造函数都必须和他兼容。可以在一个构造函数中使用new初始化指针,在另一个构造函数中将指针初始化为空(0或者c++11的nullptr,这是因为delete/delete[]可以用于空指针
- 应当使用复制构造函数,通过深度复制实现将一个对象初始化另一个对象,即复制构造函数必须分配足够的空间存储复制的数据,并复制数据,而不仅仅是数据的地址
StringBad & StringBad::operator=(const StringBad & s)
{
if (this == &s)
return *this;
delete[]str;//清空原来的字符串
len = s.len;
str = new char[len + 1];
std::strcpy(str, s.str);
return *this;
}
- 应当定义赋值运算符,通过深度复制将一个对象复制给另一个对象
StringBad & StringBad::operator=(const char * s)
{
delete[]str;//清空原来的字符串
len = std::strlen(s);
str = new char[len + 1];
std::strcpy(str, s);
return *this;
}
关于返回对象:
- 返回指向const对象的引用
使用const对象的引用的目的在于提高效率, 直接返回对象将调用复制构造函数,而返回引用不会但是这种方式也存在一些限制。
适用范围:函数返回(对象作为参数,或者通过调用对象的方法)传递给他的对象。 比如:
const Vector &Max(const Vector&v1,const Vector&v2); - 返回指向非const对象的引用
两种情形:重载赋值运算符和重载与cout一起用的<<,前者旨在提高效率,后者是必须这样做。
operator=()的返回值用于连续赋值:
String s1(“qwerty”);
String s2,s3;
s3=s2=s1;
operator<<()的返回值用于串拼输出:
String s1( hahaha);
cout<<s1<<“is coming”<<endl;
其中operator<<(cout,s1)的返回值成为一个用于显示字符串"is comming"的对象。返回类型必须是ostrean&,而不能仅仅是ostream。如果返回类型ostream,将要求调用ostream类的复制构造函数,而ostream没有公有的复制构造函数。 - 返回对象
如果返回的对象是被调用函数内的定义的局部变量,则不应该按引用的方式返回,因为在被调用函数执行完毕时,局部对象将调用其析构函数。这种情况应该返回对象而不是引用。通常被重载的算术运算符使用返回对象的方式
Vector Vector::operator+(const Vector &b)const
{
return Vector(x+b.x,y+x.y);
}
这样存在调用复制构造函数来创建被返回对象的时间开销,是不可避免的。 - 返回const对象
一般不常用
关于使用指向对象的指针
- 首先,使用new为整个对象分配内存,即保存字符串地址的str指针和len分配内存(并没有给num_string成员分配内存,因为它是静态成员,独立于对象被保存)。创建对象需要调用构造函数,后者分配用于保存字符串的内存,并将字符串的地址赋值给str。
//使用new初始化对象: String *favorite =new String(saying[choice]);
- 然后,当程序不再需要该对象时,使用delete释放它。对象是单个的,因此,使用不带中括号的delete。与前面相同,这将释放用于保存str指针和len成员的空间,并不释放str指向的内存,而该任务由析构函数来完成。
delete favorite;
指针和对象小结:
-
使用常规表示法来声明指向对象的指针:
String *glamour; -
使用将指针初始化为指向已有的对象:
String*first=&saying[0]; -
使用new来初始化指针,这将创建一个新的对象:
String *favorite = new String(phl); -
对类使用new将调用构造函数来初始化新创建的对象:
String *sleep=new String;
String *glop=new String(“hahaha”);
String *favorite = new String(phl); -
可以使用->运算符通过指针访问类的方法:
favorite->length(); -
可以对对象使用指针应用解除引用运算符(*)来获取对象
(*favorite).length();