C++入门学习九 类

第十一章 定义自己的数据类型

定义一个类就是定义一个新的数据类型。

面向对象编程的特点:

封装(特性一):定义一种数据类型为一个类,然后把和这种数据类型相关的数据和函数打包到这个类中

数据隐藏:在一般情况下,不允许访问对象的数据值。

注意:封装和数据隐藏在其他资料中经常放一块说表明,表示为"封装"这一个特性

继承(特性二):可以在一个类的基础上,续写另一个类,最开始的类叫基类,续写的类叫派生类,派生类有基类的所有属性和方法,并且可以增加自己独有的特性。

重写:在派生类中定义基类的函数

多态性:不同时刻有不同的形态。必须是基类指针调用虚函数,且派生类对该虚函数重写。

定义类

使用class关键字定义类,类名使用大写,类成员在花括号中,注意,右花括号后面必须有分号

类的所有成员,默认私有。可以用访问修饰符关键字public,private,protected定义不同的成员。

class ClassName
{
private:
	// 类的私有成员
public:
	// 类的共有成员
protected:
	// 类的受保护成员
};

注意:还可以使用关键字struct定义结构体,结构体默认成员是共有的。

struct StructName
{
private:
	// 结构体的私有有成员
public:
	// 结构体的共有成员
protected:
	// 结构体的受保护成员
};

注意:如果没有给指针类型或基本类型的成员变量指定初始值,它们就会包含垃圾值。

#include <iostream>
#include <string>

class Student
{
private:
	// 类的私有成员
	std::string name;
	int age;
public:
	// 类的共有成员
	void printInfo() {
		std::cout << "name : " << name << std::endl;
		std::cout << "age : " << age << std::endl;
	}
protected:
	// 类的受保护成员
};

int main()
{
	Student s1;
	s1.printInfo();  // 垃圾值:age :  -858993460
}

构造函数

构造函数在定义类的新实例时调用。构造函数与类的名字同名。构造函数没有返回值,也没有返回类型。

#include <iostream>
#include <string>

class Student
{
private:
	// 类的私有成员
	std::string name;
	int age;
public:
	// 类的共有成员
	Student(std::string nameValue, int ageValue) {
		std::cout << "---创建一个新学生---" << std::endl;
		name = nameValue;
		age = ageValue;
	}

	void printInfo() {
		std::cout << "name : " << name << std::endl;
		std::cout << "age : " << age << std::endl;
	}
protected:
	// 类的受保护成员
};

int main()
{
	Student s1{"XiaoHong", 12};
	s1.printInfo();
}

默认构造函数

如果没有为类定义任何构造函数。编译器将生成默认构造函数。称为默认的默认构造函数。

可以使用default告诉编译器生成一个默认的构造函数

Student() {} // 不需要分号
Student() = default; // 需要分号

成员函数的定义,可以放在类定义的外部。但是需要用类名来限定

类定义内部的成员函数被隐式申明为内联函数

// student.h
#ifndef STUDENT_H
#define STUDENT_H
#endif

#include <string>
#include <iostream>

class Student
{
private:
	// 类的私有成员
	std::string name;
	int age;
public:
	// 类的共有成员
	Student() = default;
	Student(std::string nameValue, int ageValue);
	void printInfo();
};
// student.cpp
#include "student.h"

Student::Student(std::string nameValue, int ageValue) {
	name = nameValue;
	age = ageValue;
}

void Student::printInfo() {
	std::cout << "name : " << name << std::endl;
	std::cout << "age : " << age << std::endl;
}

int main()
{
	Student s1{"XiaoHong", 12};
	s1.printInfo();
}

成员初始化列表

Student::Student(std::string nameValue, int ageValue):
	// 成员初始化列表,更高效,
	name{nameValue},
	age{ageValue}
{}

类的构造函数只有一个,可能会有问题。编译器可以使用构造函数把其他类型的值转换成类对象

// student.h
#ifndef STUDENT_H
#define STUDENT_H
#endif

#include <string>
#include <iostream>

class Student
{
private:
	// 类的私有成员
	int age;
public:
	// 类的共有成员
	Student() = default;
	Student(int num);
	int doubleAge(); // 计算年龄的两倍
	bool isLargeDoubleAge(Student student);
};
// student.cpp
#include "student.h"

Student::Student(int ageValue):
	// 成员初始化列表,更高效,
	age{ageValue}
{}

int Student::doubleAge() {
	return age * 2;
}

bool Student::isLargeDoubleAge(Student student) {
	if (doubleAge() > student.doubleAge()) {
		std::cout << doubleAge() << " > " << student.doubleAge() << std::endl;
		return true;
	}
	std::cout << doubleAge() << " <= " << student.doubleAge() << std::endl;
	return false;
}

int main()
{
	Student s1{12};
	Student s2{13};
	s1.isLargeDoubleAge(s2);
	s1.isLargeDoubleAge(10); // 假设想要与10直接进行比较,但是实际上隐式转换成了age是10的对象,再与进行doubleAge()计算后的值比较
	s1.isLargeDoubleAge(14);
}

可以对构造函数只用explicit来规避这样的问题,编译器就不会编译,直接报错。

C++中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的

explicit Student(int num);

委托构造函数

在构造函数的成员初始化列表中调用前一个构造函数的所有的值

// student.h
...
Student(int ageValue, int highValue);
Student(int value);
...

// student.cpp
...
Student::Student(int value) :
	// 委托构造,是用上一个构造函数参数
	Student{value, value / 2}
{}
...

副本构造函数

把类对象作为参数的构造函数

Student(const Student &student); // 一定要用引用,不然会无限递归调用构造函数,编译器出错


...

Student::Student(const Student &student) :
	// 副本构造+委托构造
	Student{student.age , student.high}
{}

访问私有类成员

可以增加public成员函数,直接返回私有成员变量。这些成员通常直接定义在类中,默认为内联函数。

this指针

在执行任何成员函数时,该成员函数自动包含一个隐藏的指针,称为this指针,包含调用该成员函数对象的地址。

方法链

	Student* setAge(int ageValue) {
		if (age > 0) {
			age = ageValue;
		}
		return this;
	}

	Student* setHigh(int highValue) {
		if (high > 0) {
			high = highValue;
		}
		return this;
	}


	s4.setAge(18)->setHigh(180);

const成员函数

如果定义了const对象,调用成员函数printInfo,哪怕printInfo并没有修改成员变量的值,但是,编译器并不知道,还是会报错。

void Student::printInfo() {
	std::cout << age << std::endl;
	std::cout << high << std::endl;
}

const Student s1{12, 180};

s1.printInfo(); // 报错,编译不过

这时需要给不修改成员变量的函数增加用const定义。当然无法对set这种实际修改了成员变量值得函数定义为const,编译器会报错,这个叫"cosnt 正确性"

void printInfo() const;

将对象转换为非常量

const_cast<Type*>()

const_cast<Type&>()

关键字mutable

即使是const,其const成员也可以修改

如果不加mutable会报错

mutable int num;


void setNum(int numValue) const{
	if (numValue > 0) {
		num = numValue;
	}
	return;
}

友元函数

不想让外界访问对象的内部状态,但允许选定少数相关函数访问。

一般当函数需要访问两个不同对象的内部时,才需要把该函数声明为两个类的友元函数。

关键字friend

友元类

在一个类中直接声明一个友元类

class Student
{
private:
	// 类的私有成员
	int age;
	int high;
	mutable int num;
public:
	// 类的共有成员

	friend class Teacher;

类的数组对象

前4个拷贝构造,后两个默认构造

Student ss[6]{ s1,s2,s3,s4 };

类对象的大小

成员变量大小的总和

sizeof()

类的静态成员

独立于类类型的任何对象

初始化时一般声明为inline

static inline int count{ 0 };

如果不想声明为inline,可以在内外初始化

// student.h

class Student
{
private:
	// 类的私有成员
	
	int age;
	int high;
	mutable int num;
	static int count;

// student.cpp
int Student::count {0};

静态常量

static inline const int subNo{ 111 };

静态成员函数

static void printSome() {
		std::cout << "hello" << std::endl;
	}

析构函数

默认析构函数

类的析构函数没有返回值和返回类型

~ClassName();

~Student() = default;

定义在类外部

但是需要在类中先声明

~Student();
Student::~Student() {}

使用指针作为类成员

RAII(资源获取即使化)

应该尽可能地使用std::make_unique<>()和std::make_shared<>()

实现一个Truckload

// main.cpp
#include "truckload.h"

inline unsigned random(size_t count) {
	return 1 + static_cast<unsigned>(std::rand() / (RAND_MAX / count + 1));
}

inline SharedBox randomBox() {
	const size_t dimLimit{ 99 };
	return std::make_shared<Box>(random(dimLimit), random(dimLimit), random(dimLimit));
}

int main() {
	std::srand(static_cast<unsigned>(std::time(nullptr)));

	Truckload load1;

	const size_t boxCount{ 12 };
	for (size_t i{}; i < boxCount; ++i) {
		load1.addBox(randomBox());
	}

	std::cout << "The first list:\n";
	load1.listBoxes();

	Truckload copy{ load1 };
	std::cout << "The copied truckload:\n";
	copy.listBoxes();

	SharedBox largestBox{ load1.getFirstBox() };

	SharedBox nextBox{ load1.getNextBox() };
	while (nextBox) {
		if (nextBox->compare(*largestBox) > 0) {
			largestBox = nextBox;
		}
		nextBox = load1.getNextBox();
	}

	std::cout << "\nThe largest box in the first list is ";
	largestBox->listBox();
	std::cout << std::endl;
	load1.removeBox(largestBox);
	std::cout << "\nAfter deleting the largest box,the list contains:\n";
	load1.listBoxes();

	const size_t nBoxes{ 20 };
	std::vector<SharedBox> boxes;
	for (size_t i{}; i < nBoxes; ++i) {
		boxes.push_back(randomBox());
	}
	Truckload load2{ boxes };
	std::cout << "\nThe second list:\n";
	load2.listBoxes();

	auto smallestBox = load2.getFirstBox();
	for (auto nextBox = load2.getNextBox(); nextBox; nextBox = load2.getNextBox()) {
		if (nextBox->compare(*smallestBox) < 0) {
			smallestBox = nextBox;
		}
	}
	std::cout << "\nThe smallest box in the second list is";
	smallestBox->listBox();
	std::cout << std::endl;
}

// truckload.cpp
#include "truckload.h"

Truckload::Truckload(const std::vector<SharedBox>& boxes) {
	for (const auto& pBox : boxes) {
		addBox(pBox);
	}
}

Truckload::Truckload(const Truckload& src) {
	for (Package* package{ src.pHead }; package;package = package->getNext()) {
		addBox(package->getBox());
	}
}

void Truckload::addBox(SharedBox pBox) {
	auto pPackage = new Package{ pBox };
	if (pTail) {
		pTail->setNext(pPackage);
	}
	else {
		pHead = pPackage;
	}
	pTail = pPackage;
}

void Truckload::listBoxes() const
{
	const size_t boxesPerLine = 5;
	size_t count{};
	Package* currentPackage{ pHead };
	while (currentPackage)
	{
		currentPackage->getBox()->listBox();
		if (!(++count % boxesPerLine)) {
			std::cout << std::endl;
		}
		currentPackage = currentPackage->getNext();
	}
	if (count % boxesPerLine) {
		std::cout << std::endl;
	}
}

SharedBox Truckload::getFirstBox() {
	pCurrent = pHead;
	return pCurrent ? pCurrent->getBox() : nullptr;
}

SharedBox Truckload::getNextBox() {
	if (!pCurrent) {
		return getFirstBox();
	}
	pCurrent = pCurrent->getNext();

	return pCurrent ? pCurrent->getBox() : nullptr;
}

bool Truckload::removeBox(SharedBox boxToRemove) {
	Package* previous{ nullptr };
	Package* current{ pHead };
	while (current) {
		if (current->getBox() == boxToRemove) {
			if (previous) {
				previous->setNext(current->getNext());
			}
			else {
				pHead = current->getNext();
			}
			current->setNext(nullptr);
			delete current;
			return true;
		}
		previous = current;
		current = current->getNext();
	}
	return false;
}

// truckload.h
#ifndef TRUCKLOAD_H
#define TRUCKLOAD_H
#include <vector>
#include "package.h"
#include "box.h"

class Truckload
{
private:
	Package* pHead{};
	Package* pTail{};
	Package* pCurrent{};

public:
	Truckload() = default;
	Truckload(SharedBox pBox) {
		pHead = pTail = new Package{pBox};
	}
	Truckload(const std::vector<SharedBox>& boxes);
	Truckload(const Truckload& src);

	~Truckload() { delete pHead; }

	SharedBox getFirstBox();
	SharedBox getNextBox();
	void addBox(SharedBox pBox);
	bool removeBox(SharedBox pBox);
	void listBoxes() const;
};

#endif

// package.h
#ifndef PACKAGE_H
#define PACKAGE_H
#include "box.h"

using SharedBox = std::shared_ptr<Box>;

class Package
{
private:
	SharedBox pBox;
	Package* pNext;

public:
	Package(SharedBox pb):
		pBox{pb},
		pNext{ nullptr}
	{}
	~Package() {
		delete pNext;
	}

	SharedBox getBox() const {
		return pBox;
	}

	Package* getNext() {
		return pNext;
	}
	void setNext(Package* pPackage) {
		pNext = pPackage;
	}
};
#endif

// box.h
#ifndef BOX_H
#define BOX_H
#include <iostream>
#include <iomanip>

class Box
{
private:
	double length{ 1.0 };
	double width{ 1.0 };
	double height{ 1.0 };

public:
	Box(double lengthValue, double widthValue, double heightValue):
		length{ lengthValue },
		width{ widthValue },
		height{ heightValue }
	{}

	Box() = default;

	double volume() const {
		return length * width * height;
	}

	int compare(const Box& box) const {
		if (volume() < box.volume()) {
			return -1;
		}
		if (volume() == box.volume()) {
			return 0;
		}
		return 1;
	}

	void listBox() const {
		std::cout << "Box(" << std::setw(2) << length << ','
							<< std::setw(2) << width << ','
							<< std::setw(2) << height << ')';
	}
};
#endif

// 结果
The first list:
Box(79,63,68)Box(28,66, 1)Box(83, 3,95)Box(53,80,68)Box(91,47,36)
Box(25,99,11)Box(34,73,29)Box(99,66, 7)Box(92,50,85)Box(48,96,87)
Box(99,72,22)Box( 4,65,59)
The copied truckload:
Box(79,63,68)Box(28,66, 1)Box(83, 3,95)Box(53,80,68)Box(91,47,36)
Box(25,99,11)Box(34,73,29)Box(99,66, 7)Box(92,50,85)Box(48,96,87)
Box(99,72,22)Box( 4,65,59)

The largest box in the first list is Box(48,96,87)

After deleting the largest box,the list contains:
Box(79,63,68)Box(28,66, 1)Box(83, 3,95)Box(53,80,68)Box(91,47,36)
Box(25,99,11)Box(34,73,29)Box(99,66, 7)Box(92,50,85)Box(99,72,22)
Box( 4,65,59)

The second list:
Box(34,94,24)Box(33,97,72)Box(66,52,56)Box(21,75,74)Box(36, 1, 9)
Box(59,80,31)Box(64,78,91)Box(68,56,36)Box(47,23,46)Box(67, 4, 4)
Box(40,97,18)Box(44,37,97)Box(17,97,25)Box(55,83,42)Box(13,17,47)
Box(14,35,34)Box(94,97,40)Box(40,22,26)Box(85,27,13)Box(69,16,54)

The smallest box in the second list isBox(36, 1, 9)

模仿上面写一个相似的链表

// hero.h
#ifndef HERO_H
#define HERO_H

#include <string>
#include <iostream>
#include <iomanip>

class Hero {
private:
	std::string name;
	int strength;
	int agility;
	int intelligence;

public:
	Hero(std::string nameVal, int strengthVal, int agilityVal, int intelligenceVal):
		name { nameVal },
		strength{ strengthVal },
		agility{ agilityVal },
		intelligence{ intelligenceVal }
	{}
	Hero() = default;

	int totalAttribute() const{
		return strength + agility + intelligence;
	}

	int compare(const Hero& hero) {
		if (totalAttribute() > hero.totalAttribute()) {
			return 1;
		}
		if (totalAttribute() < hero.totalAttribute()) {
			return -1;
		}
		return 0;
	}

	void listHero() {
		std::cout << "name:" << name << "\n"
			<< "strength:" << strength << "   "
			<< "agility:" << agility << "   "
			<< "intelligence:" << intelligence << std::endl;
	}
};
#endif // !HERO_H


// package.h
#ifndef PACKAGE_H
#define PACKAGE_H

#include "hero.h"
using SharedHero = std::shared_ptr<Hero>;

class Package
{
private:
	SharedHero pHero;
	Package* pNext;

public:
	Package(SharedHero pHeroVal):
		pHero{pHeroVal},
		pNext{nullptr}
	{}
	Package() = default;
	~Package() {}


	SharedHero getHero() {
		return pHero;
	}

	Package* getNext() {
		return pNext;
	}

	void setNext(Package* pPackage) {
		pNext = pPackage;
	}
};
#endif // PACKAGE_H

// team.h
#ifndef TEAM_H
#define TEAM_H

#include "package.h"
#include <vector>

class Team
{
private:
	Package* pHead{};
	Package* pTail{};
	Package* pCurrent{};

public:
	Team(SharedHero pHero) {
		pHead = pTail = new Package{ pHero };
	}
	Team(std::vector<SharedHero> hears);
	Team(const Team& team);
	Team() = default;

	void listBoxes();
	SharedHero getFirstHero();
	SharedHero getNextHero();
	void addHero(SharedHero pHero);
	bool removeHero(SharedHero pHero);
};
#endif

// team.cpp
#include "team.h"

Team::Team(std::vector<SharedHero> heros) {
	for (size_t i = 0; i < heros.size(); ++i) {
		addHero(heros[i]);
	}
}

Team::Team(const Team& team) {
	for (Package* pPackage = team.pHead; pPackage;pPackage = pPackage->getNext()) {
		addHero(pPackage->getHero());
	}
}

void Team::listBoxes() {
	Package* currentPackage = pHead;
	while (currentPackage) {
		currentPackage->getHero()->listHero();
		currentPackage = currentPackage->getNext();
	}
}

SharedHero Team::getFirstHero() {
	pCurrent = pHead;
	return pCurrent ? pCurrent->getHero() : nullptr;
}

SharedHero Team::getNextHero() {
	if (!pCurrent) {
		return getFirstHero();
	}
	pCurrent = pCurrent->getNext();
	return pCurrent ? pCurrent->getHero() : nullptr;
}

void Team::addHero(SharedHero pHero) {
	auto pPackage = new Package{pHero};
	if (pTail) {
		pTail->setNext(pPackage);
	}
	else {
		pHead = pPackage;
	}
	pTail = pPackage;
}

bool Team::removeHero(SharedHero pMoveHero) {
	Package* current = pHead;
	Package* previous = nullptr;
	while (current) {
		if (current->getHero() == pMoveHero) {
			if (previous) {
				previous->setNext(current->getNext());
			}
			else {
				pHead = current->getNext();
			}
			std::cout << "remove success!" << std::endl;
			return true;
		}
		previous = current;
		current = current->getNext();
	}
	std::cout << "error!have no want to remove hero!" << std::endl;
	return false;
}

// main.cpp
#include "hero.h"
#include "package.h"
#include "team.h"

int main() {
	std::vector<SharedHero> heroList{};
	Hero hero1;
	Hero hero2;
	Hero hero3;
	hero1 = Hero("Ember Spirit", 22, 22, 20);
	hero2 = Hero("Storm Spirit", 21, 22, 23);
	hero3 = Hero("Puck", 17, 22, 23);
	SharedHero pHero1 = std::make_shared<Hero>(hero1);
	SharedHero pHero2 = std::make_shared<Hero>(hero2);
	SharedHero pHero3 = std::make_shared<Hero>(hero3);
	heroList.push_back(pHero1);
	heroList.push_back(pHero2);

	Team team1(heroList);
	Team team2(team1);
	team1.getFirstHero()->listHero();
	SharedHero nHero = team1.getNextHero();
	while (nHero) {
		nHero->listHero();
		nHero = team1.getNextHero();
	}
	team1.removeHero(pHero1);
	team1.removeHero(pHero3);
	std::cout << "after remove!!!" << std::endl;
	team1.getFirstHero()->listHero();
	SharedHero nHero2 = team1.getNextHero();
	while (nHero2) {
		nHero2->listHero();
		nHero2 = team1.getNextHero();
	}
	std::cout << "team2:" << std::endl;
	team2.getFirstHero()->listHero();
	SharedHero nHero3 = team2.getNextHero();
	while (nHero3) {
		nHero3->listHero();
		nHero3 = team2.getNextHero();
	}


}

// res
/*
name:Ember Spirit
strength:22   agility:22   intelligence:20
name:Storm Spirit
strength:21   agility:22   intelligence:23
remove success!
error!have no want to remove hero!
after remove!!!
name:Storm Spirit
strength:21   agility:22   intelligence:23
team2:
name:Ember Spirit
strength:22   agility:22   intelligence:20
name:Storm Spirit
strength:21   agility:22   intelligence:23
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值