每日疑问:为什么大学要用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(这种形式的学名叫做初始化列表)
最后不要忘了打上一对 {}
简单总结一下:
- 构造函数的名称与类(Class)名称完全一致
- 在构造函数前有关键字 “explicit”,但是没有返回类型(因为构造函数没有返回值)
- 第一个括号内写明初始化传入的变量类型和名称
- 用 “:” 连接前后两个部分
- 第二部分满足形式 “a(x)”,即用x的值初始化class中的变量a
- 最后是一对{} (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的实现有一点复杂,需要注意以下几点:
- 头文件中包含
#include "createData.h"
- 可以使用
using namespace std;
- 声明构造函数的时候没有关键词 “explicit”
- 在所有函数名前添加 类名称::
- 没有主程序 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;
}