21天学C++(十)高级函数

完整目录、平台简介、安装环境及版本:参考《21天学C++ 概览》

十、高级函数

10.1 成员函数重载、默认值

10.1.1 成员函数重载

同样名字但是不同参数编写两个或多个函;数来实现函数,用同样的方式也可以实现重载成员函数;

#include <iostream>

class Rectangle
{
public:
	Rectangle(int width, int height);
	~Rectangle(){}

	void DrawShape() const;
	void DrawShape(int aWidth, int aHeight) const;

private:
	int itsWidth;
	int itsHeight;
};

Rectangle::Rectangle(int width, int height)
{
	itsWidth = width;
	itsHeight = height;
}

void Rectangle::DrawShape() const
{
	DrawShape( itsWidth, itsHeight);
}

void Rectangle::DrawShape(int width, int height) const
{
	for (int i = 0; i<height; i++)
	{
		for (int j = 0; j< width; j++)
		{
			std::cout << "*";
		}
		std::cout << std::endl;
	}
}

int main()
{
	// initialize a rectangle to 30,5
	Rectangle theRect(30,5);
	std::cout << "DrawShape():" << std::endl;
	theRect.DrawShape();
	std::cout << "\nDrawShape(40,2):" << std::endl;
	theRect.DrawShape(40,2);
	return 0;
}

执行结果如下:

DrawShape():
******************************
******************************
******************************
******************************
******************************

DrawShape(40,2):
****************************************
****************************************
请按任意键继续. . .

10.1.2 成员函数默认值

限制也同样适用:如果某个参数没有默认值,那么它前面的所有参数均不得有默认值,即默认值需要从右向左设置;

#include <iostream>

using namespace std;

class Rectangle
{
public:
	Rectangle(int width, int height);
	~Rectangle(){}
	void DrawShape(int aWidth, int aHeight, 
		bool UseCurrentVals = false) const;

private:
	int itsWidth;
	int itsHeight;
};

Rectangle::Rectangle(int width, int height):
itsWidth(width),       // initializations
itsHeight(height)
{}                     // empty body


void Rectangle::DrawShape(
						  int width,
						  int height,
						  bool UseCurrentValue
						  ) const
{
	int printWidth;
	int printHeight;

	if (UseCurrentValue == true)
	{
		printWidth = itsWidth;       // use current class values
		printHeight = itsHeight;
	}
	else
	{
		printWidth = width;         // use parameter values
		printHeight = height;
	}


	for (int i = 0; i<printHeight; i++)
	{
		for (int j = 0; j< printWidth; j++)
		{
			cout << "*";
		}
		cout << endl;
	}
}

int main()
{
	// initialize a rectangle to 30,5
	Rectangle theRect(30,5);
	cout << "DrawShape(0,0,true)..." << endl;
	theRect.DrawShape(0,0,true);
	cout <<"DrawShape(40,2)..." << endl;
	theRect.DrawShape(40,2);
	return 0;
}

执行结果如下:

DrawShape(0,0,true)...
******************************
******************************
******************************
******************************
******************************
DrawShape(40,2)...
****************************************
****************************************
请按任意键继续. . .

10.1.3 默认值和重载函数如何选择

重载函数更容易理解,使用起来也更为方便;

重载函数扩展也很容易,比如由两个参数修改成三个参数;

以下函数使用函数重载:

  • 没有合理的默认值;
  • 需要不同的算法;
  • 需要在参数中支持各种类型。

10.2 默认、重载、复制构造函数

10.2.1 默认构造函数

特点:

  • 没有参数、不执行任何操作;
  • 如果没有显式的声明构造函数,系统会提供一个默认构造函数;
  • 可以创建自己的默认构造函数,根据需要建立对象(初始化成员数据);
  • 如果自己创建了任何构造函数,系统将不在提供默认构造函数。

10.2.2 重载构造函数

构造函数也可以重载,很强而且很灵活;

#include <iostream>
using namespace std;

class Rectangle
{
public:
	Rectangle();
	Rectangle(int width, int length);
	~Rectangle() {}
	int GetWidth() const { return itsWidth; }
	int GetLength() const { return itsLength; }
private:
	int itsWidth;
	int itsLength;
};

Rectangle::Rectangle()
{
	itsWidth = 5;
	itsLength = 10;
}

Rectangle::Rectangle (int width, int length)
{
	itsWidth = width;
	itsLength = length;
}

int main()
{
	Rectangle Rect1;
	cout << "Rect1 width: " << Rect1.GetWidth() << endl;
	cout << "Rect1 length: " << Rect1.GetLength() << endl;

	int aWidth, aLength;
	cout << "Enter a width: ";
	cin >> aWidth;
	cout << "\nEnter a length: ";
	cin >> aLength;

	Rectangle Rect2(aWidth, aLength);
	cout << "\nRect2 width: " << Rect2.GetWidth() << endl;
	cout << "Rect2 length: " << Rect2.GetLength() << endl;
	return 0;
}

执行结果如下:

Rect1 width: 5
Rect1 length: 10
Enter a width: 6

Enter a length: 8

Rect2 width: 6
Rect2 length: 8
请按任意键继续. . .

10.2.3 初始化对象

  • 构造函数分两个阶段:初始化阶段和函数体执行阶段;
  • 有些变量必须被初始化而不能被赋值,如常量和引用;
Rectangle::Rectangle():
    itsWidth(5),
    itsLength(10){
}

Rectangle::Rectangle (int width, int length):
    itsWidth(width),
    itsLength(length){
}

10.2.4 复制构造函数

每次复制一个对象时会被调用;(包括传递到函数内或作为函数返回值)

格式:Cat(const Cat & theCat);

  • 有一个参数,该参数为对同一个类的对象的引用;
  • 赋值构造函数不必修改传进来的对象,所以使引用为常量;

默认复制构造函数,成员拷贝或浅拷贝

  • 把作为参数的对象中的每一个成员变量复制到新对象的成员变量中;
  • 对于成员变量指向自由存储区的对象的指针来说,会破坏成员变量;
  • 成员复制把一个对象成员变量精确复制到另一个对象中,成员变量指针最后均指向同一内容;

深拷贝:把堆上分配的值复制到新分配的内存中

浅拷贝在有成员变量指向自由存储区的对象指针时,会导致灾难性后果,如析构函数会释放并清理这块内存,而浅拷贝会导致另一个对象中的指针依旧指向这段内存,从而产生迷途指针,程序将处于崩溃的边缘。

解决办法:创建自己的复制构造函数并根据需要分配内存。在分配内存后,原来的值就可以复制到新的内存中,这就叫做深层复制。

#include <iostream>
using namespace std;

class Cat
{
public:
	Cat();              // default constructor
	~Cat();             // destructor
	int GetAge()   const   { return *itsAge; }
	int GetWeight() const  { return *itsWeight; }
	void SetAge(int age)   { *itsAge = age; }

private:
	int *itsAge;
	int *itsWeight;
};

Cat::Cat()
{
	itsAge = new int;
	itsWeight = new int;
	*itsAge = 5;
	*itsWeight = 9;
}

Cat::~Cat()
{
	delete itsAge;
	itsAge = 0;
	delete itsWeight;
	itsWeight = 0;
}

int main()
{
	Cat Frisky;
	cout << "Frisky's age: " << Frisky.GetAge() << endl;
	cout << "Setting Frisky to 6...\n";
	Frisky.SetAge(6);
	cout << "Creating Boots from Frisky\n";
	Cat Boots(Frisky);
	cout << "Frisky's age: " <<     Frisky.GetAge() << endl;
	cout << "Boots' age: " << Boots.GetAge() << endl;
	cout << "setting Frisky to 7...\n";
	Frisky.SetAge(7);
	cout << "Frisky's age: " <<     Frisky.GetAge() << endl;
	cout << "boot's age: " << Boots.GetAge() << endl;
	return 0;
}

执行结果如下:

自定义复制构造函数

#include <iostream>
using namespace std;

class Cat
{
public:
	Cat();              // default constructor
	Cat (const Cat &);  // copy constructor
	~Cat();             // destructor
	int GetAge()   const   { return *itsAge; }
	int GetWeight() const  { return *itsWeight; }
	void SetAge(int age)   { *itsAge = age; }

private:
	int *itsAge;
	int *itsWeight;
};

Cat::Cat()
{
	itsAge = new int;
	itsWeight = new int;
	*itsAge = 5;
	*itsWeight = 9;
}

Cat::Cat(const Cat & rhs)
{
	itsAge = new int;
	itsWeight = new int;
	*itsAge = rhs.GetAge();  // public access
	*itsWeight = *(rhs.itsWeight); // private access
}

Cat::~Cat()
{
	delete itsAge;
	itsAge = 0;
	delete itsWeight;
	itsWeight = 0;
}

int main()
{
	Cat Frisky;
	cout << "Frisky's age: " << Frisky.GetAge() << endl;
	cout << "Setting Frisky to 6...\n";
	Frisky.SetAge(6);
	cout << "Creating Boots from Frisky\n";
	Cat Boots(Frisky);
	cout << "Frisky's age: " <<     Frisky.GetAge() << endl;
	cout << "Boots' age: " << Boots.GetAge() << endl;
	cout << "setting Frisky to 7...\n";
	Frisky.SetAge(7);
	cout << "Frisky's age: " <<     Frisky.GetAge() << endl;
	cout << "boot's age: " << Boots.GetAge() << endl;
	return 0;
}

执行结果如下:

Frisky's age: 5
Setting Frisky to 6...
Creating Boots from Frisky
Frisky's age: 6
Boots' age: 6
setting Frisky to 7...
Frisky's age: 7
boot's age: 6
请按任意键继续. . .

10.3 运算符重载

C++允许用户把运算符(+-*/…)添加到自己的类中

10.3.1 重载前置运算符

#include <iostream>

using namespace std;

class Counter
{
public:
	Counter();
	~Counter(){}
	int GetItsVal()const { return itsVal; }
	void SetItsVal(int x) {itsVal = x; }
	void Increment() { ++itsVal; }
	Counter operator++ ();

private:
	int itsVal;

};

Counter::Counter():
itsVal(0)
{}

Counter Counter::operator++()
{
	++itsVal;
	Counter temp;
	temp.SetItsVal(itsVal);
	return temp;
}

int main()
{
	Counter i;
	cout << "The value of i is " << i.GetItsVal() << endl;
	i.Increment();
	cout << "The value of i is " << i.GetItsVal() << endl;
	++i;
	cout << "The value of i is " << i.GetItsVal() << endl;
	Counter a = ++i;
	cout << "The value of a: " << a.GetItsVal();
	cout << " and i: " << i.GetItsVal() << endl;
	return 0;
}

执行结果如下:

The value of i is 0
The value of i is 1
The value of i is 2
The value of a: 3 and i: 3
请按任意键继续. . .

创建临时对象并返回:需要创建并释放对象

Counter Counter::operator++(){
   ++itsVal;
   Counter temp;
   temp.SetItsVal(itsVal);
   return temp;
}

创建无名临时对象并返回:需要创建并释放对象

Counter Counter::operator++(){
   ++itsVal;
   return Counter(itsVal);
}

使用this指针:返回引用,不必创建临时变量;Const避免++++i出现;

const Counter& Counter::operator++(){
   ++itsVal;
   return *this;
}

10.3.2 重载后置运算符

后置运算符必须在加1以前返回已有的值;

#include <iostream>

using namespace std;

class Counter
{
public:
	Counter();
	~Counter(){}
	int GetItsVal()const { return itsVal; }
	void SetItsVal(int x) {itsVal = x; }
	const Counter& operator++ ();      // prefix
	const Counter operator++ (int); // postfix

private:
	int itsVal;
};

Counter::Counter():
itsVal(0)
{}

const Counter& Counter::operator++()
{
	++itsVal;
	return *this;
}

const Counter Counter::operator++(int theFlag)
{
	Counter temp(*this);
	++itsVal;
	return temp;
}

int main()
{
	Counter i;
	cout << "The value of i is " << i.GetItsVal() << endl;
	i++;
	cout << "The value of i is " << i.GetItsVal() << endl;
	++i;
	cout << "The value of i is " << i.GetItsVal() << endl;
	Counter a = ++i;
	cout << "The value of a: " << a.GetItsVal();
	cout << " and i: " << i.GetItsVal() << endl;
	a = i++;
	cout << "The value of a: " << a.GetItsVal();
	cout << " and i: " << i.GetItsVal() << endl;
	return 0;
}

执行结果如下:

The value of i is 0
The value of i is 1
The value of i is 2
The value of a: 3 and i: 3
The value of a: 3 and i: 4
请按任意键继续. . .

10.3.3 重载加法运算符

#include <iostream>

using namespace std;

class Counter
{
public:
	Counter();
	Counter(int initialValue);
	~Counter(){}
	int GetItsVal()const { return itsVal; }
	void SetItsVal(int x) {itsVal = x; }
	Counter Add(const Counter &);

private:
	int itsVal;
};

Counter::Counter(int initialValue):
itsVal(initialValue)
{}

Counter::Counter():
itsVal(0)
{}

Counter Counter::Add(const Counter & rhs)
{
	return Counter(itsVal+ rhs.GetItsVal());
}

int main()
{
	Counter varOne(2), varTwo(4), varThree;
	varThree = varOne.Add(varTwo);
	cout << "varOne: " << varOne.GetItsVal()<< endl;
	cout << "varTwo: " << varTwo.GetItsVal() << endl;
	cout << "varThree: " << varThree.GetItsVal() << endl;

	return 0;
}

执行结果如下:

varOne: 2
varTwo: 4
varThree: 6
请按任意键继续. . .
#include <iostream>

using namespace std;

class Counter
{
public:
	Counter();
	Counter(int initialValue);
	~Counter(){}
	int GetItsVal()const { return itsVal; }
	void SetItsVal(int x) {itsVal = x; }
	Counter operator+ (const Counter &);
private:
	int itsVal;
};

Counter::Counter(int initialValue):
itsVal(initialValue)
{}

Counter::Counter():
itsVal(0)
{}

Counter Counter::operator+ (const Counter & rhs)
{
	return Counter(itsVal + rhs.GetItsVal());
}

int main()
{
	Counter varOne(2), varTwo(4), varThree;
	varThree = varOne + varTwo;
	cout << "varOne: " << varOne.GetItsVal()<< endl;
	cout << "varTwo: " << varTwo.GetItsVal() << endl;
	cout << "varThree: " << varThree.GetItsVal() << endl;

	return 0;
}

执行结果如下:

varOne: 2
varTwo: 4
varThree: 6
请按任意键继续. . .

10.3.4 重载赋值运算符

编译器提供四个默认函数,构造函数、析构函数、复制构造函数和赋值运算符;

赋值也存在浅和深的问题,系统默认提供的是浅赋值,涉及到堆时,需要深赋值;

String& String::operator=(const String & rhs){
   if (this == &rhs)//自赋值
      return *this;
   delete [] itsString;
   itsLen=rhs.GetLen();
   itsString = new char[itsLen+1];
   for (unsigned short i = 0; i<itsLen;i++)
      itsString[i] = rhs[i];
   itsString[itsLen] = '\0';
   return *this;//连续赋值A=B=C
}
#include <iostream>

using namespace std;

class Cat
{
public:
	Cat();              // default constructor
	// copy constructor and destructor elided!
	int GetAge() const { return *itsAge; }
	int GetWeight() const { return *itsWeight; }
	void SetAge(int age) { *itsAge = age; }
	Cat & operator=(const Cat &);

private:
	int *itsAge;
	int *itsWeight;
};

Cat::Cat()
{
	itsAge = new int;
	itsWeight = new int;
	*itsAge = 5;
	*itsWeight = 9;
}


Cat & Cat::operator=(const Cat & rhs)
{
	if (this == &rhs)
		return *this;
	*itsAge = rhs.GetAge();
	*itsWeight = rhs.GetWeight();
	return *this;
}


int main()
{
	Cat Frisky;
	cout << "Frisky's age: " << Frisky.GetAge() << endl;
	cout << "Setting Frisky to 6...\n";
	Frisky.SetAge(6);
	Cat Whiskers;
	cout << "Whiskers' age: " << Whiskers.GetAge() << endl;
	cout << "copying Frisky to Whiskers...\n";
	Whiskers = Frisky;
	cout << "Whiskers' age: " << Whiskers.GetAge() << endl;
	return 0;
}

执行结果如下:

Frisky's age: 5
Setting Frisky to 6...
Whiskers' age: 5
copying Frisky to Whiskers...
Whiskers' age: 6
请按任意键继续. . .

10.4 装箱、拆箱

10.4.1 装箱

通过创建一个只带一个整型变量的构造函数来告诉编译器如何将整型转换成类对象;

创建临时变量,并将临时变量进行赋值;

#include <iostream>

using namespace std;

class Counter
{
public:
	Counter(){itsVal = 0; cout<<"constructor"<<endl;};
	Counter(int val){itsVal = val; cout<<"constructor"<<endl;};
	~Counter(){cout<<"destructor"<<endl;}
	int GetItsVal()const { return itsVal; }
	void SetItsVal(int x) {itsVal = x; }
private:
	int itsVal;
};

int main()
{
	int theInt = 5;
	Counter theCtr = theInt;
	cout << "theCtr: " << theCtr.GetItsVal() << endl;
	return 0;
}

执行结果如下:

constructor
theCtr: 5
destructor
请按任意键继续. . .

10.4.2 拆箱

#include <iostream>
using namespace std;
class Counter
{
public:
	Counter();
	Counter(int val);
	~Counter(){cout<<"destructor"<<endl;}
	int GetItsVal()const { return itsVal; }
	void SetItsVal(int x) {itsVal = x; }
	operator unsigned int();
private:
	int itsVal;
};

Counter::Counter():
itsVal(0)
{cout<<"constructor"<<endl;}

Counter::Counter(int val):
itsVal(val)
{cout<<"constructor"<<endl;}

Counter::operator unsigned int (){
	return ( int (itsVal) );
}

int main()
{
	Counter ctr(5);
	int theInt = ctr;
	std::cout << "theInt: " << theInt << std::endl;
	return 0;
}

执行结果如下:

constructor
theInt: 5
destructor
请按任意键继续. . .

发表回复