类和动态内存分配(2)

·## 改进后的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();

猜你喜欢

转载自blog.csdn.net/qq_29689907/article/details/84430336