浅谈C++中的类Class(真的复杂啊)

每日疑问:为什么大学要用class而歧视struct呢?
而且这么困难的内容为什么要放到第三章???
有哪位大佬可以给我讲明白吗?


简单入门

C++萌新:
什么是类(Class)?
怎么定义类?
类有没有什么高级操作呢?

面对这样的素质三联,C_T表示:不可描述,先举个栗子吧~

上栗子在这里插入图片描述

#include<iostream>
#include<string>

using namespace std;

class Data{
public:
	void setname(string x) {
		name=x;
	}
	string getname() const {
		return name;
	}
	void printhello() const {
		cout<<"Hello , "<<getname()<<"!"<<endl;
		//cout<<"Hello , "<<name<<"!"<<endl;  //这里直接调用name也是可以的
	}
private:
	string name;
};

int main()
{
	string username;
	cout<<"Please enter your name : ";
	getline(cin,username);  //读入一行到回车为止
	Data userinformation;
	userinformation.setname(username);
	userinformation.printhello();
	system("pause");
	return 0;
}

在这里插入图片描述

在这个简单的小程序里面,我们声明了一个类

class Data{
public:
	void setname(string x) {
		name=x;
	}
	string getname() const {
		return name;
	}
	void printhello() const {
		cout<<"Hello , "<<getname()<<"!"<<endl;
	}
private:
	string name;
};

总体上来说,class的声明和struct非常相像,但是在大括号中多了两个关键字:

public: 公有继承,定义在这里的内容可以在整个程序中调用(包括main和其他函数)
private: 私有继承,定义在这里的内容只能在class内部调用
protected: 保护继承,这里不做重点介绍,日后会有详细讲解

class中的小函数定义方式比较中规中矩(这里不再冗述)
需要注意一下这里

void setname(string x) 
string getname() const
//在函数名称后有const表明此函数不会改变class中变量的值

怎么样,好像还挺简单的嘛~
到这里,你会发现你已经掌握了C++大学教程第三章的前四小节,感觉是不是充满了信心呢【大雾


使用构造函数初始化对象

首先我们来看这个栗子:

#include<iostream>
#include<string>

using namespace std;

class Data{
public:
	void setname(string x) {
		name=x;
	}
	string getname() const {
		return name;
	}
	void printhello() const {
		cout<<"Hello , "<<name<<"!"<<endl;
	}
private:
	string name;
};

int main()
{
	string username;
	Data userinformation;

	cout<<"Your initial name is"<<userinformation.getname()<<endl;
	cout<<endl;

	cout<<"Please enter your name : ";
	getline(cin,username);
	userinformation.setname(username);
	userinformation.printhello();
	system("pause");
	return 0;
}

在这里插入图片描述

在这个栗子中,我干了一件什么事呢?我输出了一下在我声明userinformation时其中自带的name(即初始值)
我们可以看到,name的初始值就是一个空字符串

如果我们想在创造Data对象的时候就完成初始化的话,该怎么办呢?
实际上,在class的声明中我们可以创建一个或多个构造函数,直接帮我们完成初始化:

#include<iostream>
#include<string>

using namespace std;

class Data{
public:
	explicit Data(string x) : name(x) {}
	string getname() const {
		return name;
	}
	void printhello() const {
		cout<<"Hello , "<<name<<"!"<<endl;
	}
private:
	string name;
};

int main()
{
	string username;
	Data userinformation("Coco_T_");
	userinformation.printhello();
	system("pause");
	return 0;
}

好的我们仔细观察这个崭新的Data:

class Data{
public:
	explicit Data(string x) : name(x) {}
	string getname() const {
		return name;
	}
	void printhello() const {
		cout<<"Hello , "<<name<<"!"<<endl;
	}
private:
	string name;
};

会发现这里出现了玄学的一行代码:

explicit Data(string x) : name(x) {}

解释一下:
在定义构造函数的时候我们一般将其放在public中,名称与类名称(Data)完全相同
在最前面有关键字 “explicit”,但是没有返回类型(因为构造函数是无法返回任何值的,甚至连void都不行)
第一个括号里是初始化传入的变量类型和名称
之后用一个 “:” 连接第二部分
第二部分中 “a(x)” 的形式表明用x的值初始化class中的变量a(这种形式的学名叫做初始化列表)
最后不要忘了打上一对 {}

简单总结一下:

  1. 构造函数的名称与类(Class)名称完全一致
  2. 在构造函数前有关键字 “explicit”,但是没有返回类型(因为构造函数没有返回值)
  3. 第一个括号内写明初始化传入的变量类型和名称
  4. “:” 连接前后两个部分
  5. 第二部分满足形式 “a(x)”,即用x的值初始化class中的变量a
  6. 最后是一对{} (empty body)

定义可重复使用的类

创建类的好处在于:正确定义时,类可以被其他程序员所复用
例如程序中包含的头文件string,我们可以在C++的任何一个程序中复用C++标准类string
再者,定义类有一个好处就是,如果你给某客户开发软件
你在源代码中丢给客户一个类,客户可以直接使用这个类包含的功能,而不需要去理解这些功能是如何实现的(貌似对我们开发者没有什么必然好处啊,难道就是让客户爽嘛)

这样的话,我们就需要将类定义到一个非源代码的地方,与源代码分开
一般的,我们会编写一个 “ xxx.h ” 文件,与 “ xxx.cpp ” 存放在一个目录下
这个 “ xxx.h ” 文件就是我们定义的可重复使用类

//createData.h
#include<iostream>
#include<string>

//在自定义头文件中不应该出现using指令或using声明
//别问我为什么
//在所有出现cin,cout,endl,string的前面添加std::进行声明

class Data{
public:
	explicit Data(std :: string x) : name(x) {}
	std :: string getname() const {
		return name;
	}
	void printhello() const {
		std :: cout<<"Hello , "<<name<<"!"<<std :: endl;
	}
private:
	std :: string name;
};

//test.cpp
#include<iostream>
#include "createData.h"

using namespace std;

int main()
{
	string username;
	Data userinformation("Coco_T_");
	userinformation.printhello();
	system("pause");
	return 0;
}

注意我们在源文件cpp引用头文件的时候,是这么写的

#include "createData.h"

我们没有使用<>,而是用了 “”
正常情况下,编写的源代码文件和自定义的头文件放在一个文件夹下,
当程序读到 “” 时,就会在#include指令出现的文件所在的文件夹下寻找对应的头文件


接口与实现的分离

在上一部分中,我们实现了自定义头文件并且把头文件与源代码分开的任务
然而作为一个未来要面对“客户就是上帝”的程序员来说,
这种写法就是DD!!!

那么最高级的写法就要来咯~
首先我们还是编写一个createData.h文件
但是在creatData.h文件中,我们要装深沉,并不详细写出每一个小程序要干嘛(现在看不懂没关系,下面有可爱的代码菌)
之后我们编写一个createData.cpp文件,具体说明每个小程序的功能及实现过程
(这样干的目的实际上还是方便客户的使用,同时也可以让你的同行感到异常舒适,很诡异的做法吧。。。)
最后献上test.cpp即可

//createData.h
#include<iostream>
#include<string>
//不要出using指令
class Data{
public:
	explicit Data(std :: string x);    //这里失去了 : a(x) 的部分
	std :: string getname() const;
	void printhello() const;
private:
	std :: string name;
};

在createData.h中我们把所有大括号以及大括号内的内容隐去即可

//createData.cpp
#include<iostream>
#include<string>
#include "createData.h"

using namespace std;

Data :: Data(string x) : name(x) {}
//构造函数,失去了explicit,出现了Data::

string Data :: getname() const {
	return name;
}

void Data :: printhello() const {
	cout<<"Hello , "<<name<<"!"<<endl;
}


createData.cpp的实现有一点复杂,需要注意以下几点:

  1. 头文件中包含#include "createData.h"
  2. 可以使用using namespace std;
  3. 声明构造函数的时候没有关键词 “explicit”
  4. 在所有函数名前添加 类名称::
  5. 没有主程序 int main()
//test.cpp
#include<iostream>
#include<string>
#include "createData.h"

using namespace std;

int main()
{
	string username;
	Data userinformation("Coco_T_");
	userinformation.printhello();
	system("pause");
	return 0;
}
最后献上教材上的例题代码:
//GradeBook.h
#include<string>

class GradeBook {
public:
	explicit GradeBook(std :: string);
	void setCourseName(std :: string);
	std :: string getCourseName() const;
	void displayMessage() const;
private:
	std :: string courseName;
};
//GradeBook.cpp
#include<iostream>
#include<string>
#include "GradeBook.h"

using namespace std;

GradeBook :: GradeBook (string name) {
	setCourseName(name);
}

void GradeBook :: setCourseName(string name) {
	if (name.size()<=25) 
	    courseName=name;
	if (name.size()>25) {
		courseName=name.substr(0,25);
		cerr<<"Name \""<<name<<"\" exceeds maximum length (25).\n"
			  <<"Limit courseName to first 25 characters.\n"<<endl;
	}
}

string GradeBook :: getCourseName() const {
	return courseName;
}

void GradeBook :: displayMessage() const {
	cout<<"Welcome to the grade book for\n"<<getCourseName()<<"!"<<endl;
}
//example.cpp
#include<iostream>
#include<string>
#include "GradeBook.h"

using namespace std;

int main()
{
	GradeBook gradeBook1("CS101 Introduction to Programming in C++");
	GradeBook gradeBook2("CS102 C++ Data Structures");

	cout<<"gradeBook1's initial course name is: "<<gradeBook1.getCourseName()
		   <<"\ngradeBook2's initial course name is: "<<gradeBook2.getCourseName()<<endl;

	gradeBook1.setCourseName("CS101 C++ Programming");

	cout<<"\ngradeBook1's course name is: "<<gradeBook1.getCourseName()
		   <<"\ngradeBook2's course name is: "<<gradeBook2.getCourseName()<<endl;
	
	system("pause");
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值