C++基础教程面向对象(学习笔记(17))

综合测验

在本章中,我们探讨了C ++的本质 - 面向对象编程!这是教程系列中最重要的一章。

总结

类允许您创建自己的数据类型,这些数据类型捆绑了处理该数据的数据和函数。类中的数据和函数称为成员。通过使用选择该类的成员。运算符(或者 - >如果您通过指针访问成员)。

访问说明符允许您指定谁可以访问类的成员。公共成员可以由任何人直接访问。私人成员只能由该班级的其他成员访问。当我们获得继承时,我们将在以后介绍受保护的成员。默认情况下,类的所有成员都是私有的,结构的所有成员都是公共的。

封装是将所有成员数据设为私有的过程,因此无法直接访问。这有助于保护您的班级免遭滥用。

构造函数是一种特殊类型的成员函数,允许您初始化类的对象。不带参数(或具有所有默认参数)的构造函数称为默认构造函数。如果用户未提供初始化值,则使用默认构造函数。您应该始终为您的类提供至少一个构造函数。

成员初始化列表允许您从构造函数中初始化成员变量(而不是分配成员变量值)。

在C ++ 11中,非静态成员初始化允许您在声明成员变量时直接指定它们的默认值。

在C ++ 11之前,构造函数不应该调用其他构造函数(它将编译,但不会按预期工作)。在C ++ 11中,允许构造函数调用其他构造函数(称为委托构造函数或构造函数链接)。

析构函数是另一种特殊的成员函数,允许您的类自我清理。应该从这里执行任何类型的释放或关闭例程。

所有成员函数都有一个隐藏的* this指针,指向要修改的类对象。大多数情况下,您不需要直接访问此指针。但是如果你需要,你可以。

将类定义放在与类同名的头文件中是一种很好的编程风格,并在与该类同名的.cpp文件中定义类函数。这也有助于避免循环依赖。

如果成员函数不修改类的状态,则可以(并且应该)成为const。Const类对象只能调用const成员函数。

静态成员变量在类的所有对象之间共享。虽然可以从类对象访问它们,但也可以通过范围解析运算符直接访问它们。

类似地,静态成员函数是没有* this指针的成员函数。他们只能访问静态成员变量。

Friend函数是被视为类的成员函数的函数(因此可以直接访问类的私有数据)。朋友类是类中所有成员都被视为友元函数的类。

可以创建匿名类对象,以便在表达式中进行求值,或传递或返回值。

您还可以在类中嵌套类型。这通常与与类相关的枚举使用,但如果需要,可以使用其他类型(包括其他类)。

QUIZ time

1a)编写一个名为Point2d的类。Point2d应包含两个类型为double的成员变量:m_x和m_y,两者都默认为0.0。提供构造函数和打印功能。

应运行以下程序:


#include <iostream>
 
int main()
{
    Point2d first;
    Point2d second(3.0, 4.0);
    first.print();
    second.print();
 
    return 0;
}

这应该打印:

Point2d(0,0);
Point2d(3,4);
解决方案:

#include <iostream>
 
class Point2d
{
private:
	double m_x;
	double m_y;
 
public:
	Point2d(double x = 0.0, double y = 0.0)
		: m_x(x), m_y(y)
	{
	}
 
	void print() const
	{
		std::cout << "Point2d(" << m_x << ", " << m_y << ")\n";
	}
};
 
 
int main()
{
   Point2d first;
   Point2d second(3.0, 4.0);
   first.print();
   second.print();
 
    return 0;
}

1b)现在添加一个名为distanceTo的成员函数,它将另一个Point2d作为参数,并计算它们之间的距离。给定两个点(x1,y1)和(x2,y2),它们之间的距离可以计算为sqrt((x1-x2)(x1-x2)+(y1-y2)(y1-y2))。sqrt函数位于标题cmath中。

应运行以下程序:

int main()
{
    Point2d first;
    Point2d second(3.0, 4.0);
    first.print();
    second.print();
    std::cout << "Distance between two points: " << first.distanceTo(second) << '\n';
 
    return 0;
}

这应该打印:

Point2d(0,0);
Point2d(3,4);
Distance between two points: 5
显示解决方案

1c)将函数distanceTo从成员函数更改为非成员友元函数,该函数将两个Point作为参数。同时将其重命名为“distanceFrom”。

应运行以下程序:

int main()
{
    Point2d first;
    Point2d second(3.0, 4.0);
    first.print();
    second.print();
    std::cout << "Distance between two points: " << distanceFrom(first, second) << '\n';
 
    return 0;
}

这应该打印:

Point2d(0,0);
Point2d(3,4);
Distance between two points: 5

#include <cmath>
#include <iostream>
 
class Point2d
{
private:
	double m_x;
	double m_y;
 
public:
	Point2d(double x = 0.0, double y = 0.0)
		: m_x(x), m_y(y)
	{
	}
 
	void print() const
	{
		std::cout << "Point2d(" << m_x << " , " << m_y << ")\n";
	}
 
	friend double distanceFrom(const Point2d &x, const Point2d &y);
 
};
 
double distanceFrom(const Point2d &x, const Point2d &y)
{
	return sqrt((x.m_x - y.m_x)*(x.m_x - y.m_x) + (x.m_y - y.m_y)*(x.m_y - y.m_y));
}
 
int main()
{
	Point2d first;
	Point2d second(3.0, 4.0);
	first.print();
	second.print();
	std::cout << "Distance between two points: " << distanceFrom(first, second) << '\n';
 
    return 0;
}

2)为这个类写一个析构函数:

class HelloWorld
{
private:
	char *m_data;
 
public:
	HelloWorld()
	{
		m_data = new char[14];
		const char *init = "Hello, World!";
		for (int i = 0; i < 14; ++i)
			m_data[i] = init[i];
	}
 
	~HelloWorld()
	{
        // replace this comment with your destructor implementation
	}
 
	void print() const
	{
		std::cout << m_data;
	}
 
};
 
int main()
{
	HelloWorld hello;
	hello.print();
 
    return 0;
}

解决方案:

class HelloWorld
{
private:
	char *m_data;
 
public:
	HelloWorld()
	{
		m_data = new char[14];
		const char *init = "Hello, World!";
		for (int i = 0; i < 14; ++i)
			m_data[i] = init[i];
	}
 
	~HelloWorld()
	{
		delete[] m_data;
	}
 
	void print() const
	{
		std::cout << m_data;
	}
 
};
 
int main()
{
	HelloWorld hello;
	hello.print();
 
    return 0;
}

3)让我们创建一个随机怪物生成器。这个应该很有趣。

3a)首先,让我们创建一个名为MonsterType的怪物类型的枚举。包括以下怪物类型:龙,地精,食人魔,兽人,骷髅,巨魔,吸血鬼和僵尸(Dragon, Goblin, Ogre, Orc, Skeleton, Troll, Vampire, 和 Zombie)。添加额外的MAX_MONSTER_TYPES枚举,以便我们可以计算有多少个枚举器。
解决方案:

enum MonsterType
{
	DRAGON,
	GOBLIN,
	OGRE,
	ORC,
	SKELETON,
	TROLL,
	VAMPIRE,
	ZOMBIE,
	MAX_MONSTER_TYPES
};

3b)现在,让我们创建我们的Monster类。我们的Monster将有4个属性(成员变量):一个类型(MonsterType),一个名称(std :: string),一个咆哮(std :: string)和一个生命点数(int)。创建一个包含这4个成员变量的Monster类。

#include <string>
 
enum MonsterType
{
	DRAGON,
	GOBLIN,
	OGRE,
	ORC,
	SKELETON,
	TROLL,
	VAMPIRE,
	ZOMBIE,
	MAX_MONSTER_TYPES
};
 
class Monster
{
private:
 
	MonsterType m_type;
	std::string m_name;
	std::string m_roar;
	int m_hitPoints;
};

3c)枚举MonsterType特定于Monster,因此将类中的枚举作为公共声明移动。

#include <string>
 
class Monster
{
public:
	enum MonsterType
	{
		DRAGON,
		GOBLIN,
		OGRE,
		ORC,
		SKELETON,
		TROLL,
		VAMPIRE,
		ZOMBIE,
		MAX_MONSTER_TYPES
	};
 
private:
 
	MonsterType m_type;
	std::string m_name;
	std::string m_roar;
	int m_hitPoints;
};

3d)创建一个允许初始化所有成员变量的构造函数。

以下程序应编译:

int main()
{
	Monster skele(Monster::SKELETON, "Bones", "*rattle*", 4);
 
    return 0;
}

解决方案:

#include <string>
 
class Monster
{
public:
	enum MonsterType
	{
		DRAGON,
		GOBLIN,
		OGRE,
		ORC,
		SKELETON,
		TROLL,
		VAMPIRE,
		ZOMBIE,
		MAX_MONSTER_TYPES
	};
 
private:
 
	MonsterType m_type;
	std::string m_name;
	std::string m_roar;
	int m_hitPoints;
 
public:
	Monster(MonsterType type, std::string name, std::string roar, int hitPoints)
		: m_type(type), m_name(name), m_roar(roar), m_hitPoints(hitPoints)
	{
 
	}
};
 
int main()
{
	Monster skele(Monster::SKELETON, "Bones", "*rattle*", 4);
 
    return 0;
}

3e)现在我们希望能够打印我们的怪物,以便我们验证它是正确的。为此,我们需要编写一个将MonsterType转换为std :: string的函数。编写该函数(称为getTypeString()),以及print()成员函数。

以下程序应编译:

int main()
{
	Monster skele(Monster::SKELETON, "Bones", "*rattle*", 4);
	skele.print();
 
    return 0;
}

并打印:

Bones the skeleton has 4 hit points and says rattle

#include <iostream>
#include <string>
 
class Monster
{
public:
	enum MonsterType
	{
		DRAGON,
		GOBLIN,
		OGRE,
		ORC,
		SKELETON,
		TROLL,
		VAMPIRE,
		ZOMBIE,
		MAX_MONSTER_TYPES
	};
 
private:
 
	MonsterType m_type;
	std::string m_name;
	std::string m_roar;
	int m_hitPoints;
 
public:
	Monster(MonsterType type, std::string name, std::string roar, int hitPoints)
		: m_type(type), m_name(name), m_roar(roar), m_hitPoints(hitPoints)
	{
 
	}
 
	std::string getTypeString() const
	{
		switch (m_type)
		{
		case DRAGON: return "dragon";
		case GOBLIN: return "goblin";
		case OGRE: return "ogre";
		case ORC: return "orc";
		case SKELETON: return "skeleton";
		case TROLL: return "troll";
		case VAMPIRE: return "vampire";
		case ZOMBIE: return "zombie";
		}
	
		return "???";
	}
 
	void print() const
	{
		std::cout << m_name << " the " << getTypeString() << " has " << m_hitPoints << " hit points and says " << m_roar << '\n';
	}
};
 
int main()
{
	Monster skele(Monster::SKELETON, "Bones", "*rattle*", 4);
	skele.print();
 
    return 0;
}

3f)现在我们可以创建一个随机怪物生成器。让我们来看看我们的MonsterGenerator类是如何工作的。理想情况下,我们会要求它给我们一个怪物,它会为我们创建一个随机的怪物。我们不需要多个MonsterGenerator。这是静态类(一个所有函数都是静态的)的良好候选者。创建一个静态MonsterGenerator类。创建一个名为generateMonster()的静态函数。这应该归还怪物。现在,让它返回匿名怪物(Monster :: SKELETON,“Bones”,“* rattle *”,4);

以下程序应编译:

int main()
{
	Monster m = MonsterGenerator::generateMonster();
	m.print();
 
    return 0;
}

并打印:

Bones the skeleton has 4 hit points and says rattle

#include <iostream>
#include <string>
 
class Monster
{
public:
	enum MonsterType
	{
		DRAGON,
		GOBLIN,
		OGRE,
		ORC,
		SKELETON,
		TROLL,
		VAMPIRE,
		ZOMBIE,
		MAX_MONSTER_TYPES
	};
 
private:
 
	MonsterType m_type;
	std::string m_name;
	std::string m_roar;
	int m_hitPoints;
 
public:
	Monster(MonsterType type, std::string name, std::string roar, int hitPoints)
		: m_type(type), m_name(name), m_roar(roar), m_hitPoints(hitPoints)
	{
 
	}
 
	std::string getTypeString() const
	{
		switch (m_type)
		{
		case DRAGON: return "dragon";
		case GOBLIN: return "goblin";
		case OGRE: return "ogre";
		case ORC: return "orc";
		case SKELETON: return "skeleton";
		case TROLL: return "troll";
		case VAMPIRE: return "vampire";
		case ZOMBIE: return "zombie";
		}
	
		return "???";
	}
 
	void print() const
	{
		std::cout << m_name << " the " << getTypeString() << " has " << m_hitPoints << " hit points and says " << m_roar << '\n';
	}
};
 
class MonsterGenerator
{
public:
	static Monster generateMonster()
	{
		return Monster(Monster::SKELETON, "Bones", "*rattle*", 4);
	}
};
 
int main()
{
	Monster m = MonsterGenerator::generateMonster();
	m.print();
 
    return 0;
}

3g)现在,MonsterGenerator需要生成一些随机属性。要做到这一点,我们需要利用这个方便的功能:

	// Generate a random number between min and max (inclusive)
	// Assumes srand() has already been called
	int getRandomNumber(int min, int max)
	{
		static const double fraction = 1.0 / (static_cast<double>(RAND_MAX) + 1.0);  // static used for efficiency, so we only calculate this value once
		// evenly distribute the random number across our range
		return static_cast<int>(rand() * fraction * (max - min + 1) + min);
	}

但是,因为MonsterGenerator直接依赖于这个函数,所以我们把它作为一个静态函数放在类中。
解决方案:

class MonsterGenerator
{
public:
	// Generate a random number between min and max (inclusive)
	// Assumes srand() has already been called
	static int getRandomNumber(int min, int max)
	{
		static const double fraction = 1.0 / (static_cast<double>(RAND_MAX) + 1.0);  // static used for efficiency, so we only calculate this value once
		// evenly distribute the random number across our range
		return static_cast<int>(rand() * fraction * (max - min + 1) + min);
	}
 
	static Monster generateMonster()
	{
		return Monster(Monster::SKELETON, "Bones", "*rattle*", 4);
	}
};

3h)现在编辑函数generateMonster()以生成随机MonsterType(在0和Monster :: MAX_MONSTER_TYPES-1之间)和随机生命点(在1和100之间)。这应该是相当简单的。完成后,在函数内部定义两个大小为6的静态固定数组(名为s_names和s_roars),并使用您选择的6个名称和6个声音初始化它们。从这些数组中选择一个随机名称。

以下程序应编译:

#include <ctime> // for time()
#include <cstdlib> // for rand() and srand()
int main()
{
	srand(static_cast<unsigned int>(time(0))); // set initial seed value to system clock
	rand(); // If using Visual Studio, discard first random value
 
	Monster m = MonsterGenerator::generateMonster();
	m.print();
 
    return 0;
}
#include <ctime> // for time()
#include <cstdlib> // for rand() and srand()
#include <iostream>
#include <string>
 
class Monster
{
public:
	enum MonsterType
	{
		DRAGON,
		GOBLIN,
		OGRE,
		ORC,
		SKELETON,
		TROLL,
		VAMPIRE,
		ZOMBIE,
		MAX_MONSTER_TYPES
	};
 
private:
 
	MonsterType m_type;
	std::string m_name;
	std::string m_roar;
	int m_hitPoints;
 
public:
	Monster(MonsterType type, std::string name, std::string roar, int hitPoints)
		: m_type(type), m_name(name), m_roar(roar), m_hitPoints(hitPoints)
	{
 
	}
 
	std::string getTypeString() const
	{
		switch (m_type)
		{
		case DRAGON: return "dragon";
		case GOBLIN: return "goblin";
		case OGRE: return "ogre";
		case ORC: return "orc";
		case SKELETON: return "skeleton";
		case TROLL: return "troll";
		case VAMPIRE: return "vampire";
		case ZOMBIE: return "zombie";
		}
	
		return "???";
	}
 
	void print() const
	{
		std::cout << m_name << " the " << getTypeString() << " has " << m_hitPoints << " hit points and says " << m_roar << '\n';
	}
};
 
class MonsterGenerator
{
public:
	// Generate a random number between min and max (inclusive)
	// Assumes srand() has already been called
	static int getRandomNumber(int min, int max)
	{
		static const double fraction = 1.0 / (static_cast<double>(RAND_MAX) + 1.0);  // static used for efficiency, so we only calculate this value once
		// evenly distribute the random number across our range
		return static_cast<int>(rand() * fraction * (max - min + 1) + min);
	}
 
	static Monster generateMonster()
	{
		Monster::MonsterType type = static_cast<Monster::MonsterType>(getRandomNumber(0, Monster::MAX_MONSTER_TYPES - 1));
		int hitPoints = getRandomNumber(1, 100);
 
		static std::string s_names[6]{ "Blarg", "Moog", "Pksh", "Tyrn", "Mort", "Hans" };
		static std::string s_roars[6]{ "*ROAR*", "*peep*", "*squeal*", "*whine*", "*hum*", "*burp*"};
 
		return Monster(type, s_names[getRandomNumber(0, 5)], s_roars[getRandomNumber(0, 5)], hitPoints);
	}
};
 
int main()
{
	srand(static_cast<unsigned int>(time(0))); // set initial seed value to system clock
	rand(); // If using Visual Studio, discard first random value
 
	Monster m = MonsterGenerator::generateMonster();
	m.print();
 
    return 0;
}

3i)为什么我们将变量s_names和s_roars声明为静态?
回答:使s_names和s_roars静态导致它们只被初始化一次。否则,每次调用generateMonster()时都会重新初始化它们。

猜你喜欢

转载自blog.csdn.net/qq_41879485/article/details/83038246