📄 classabc.html
字号:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<link rel="stylesheet" href="css/stdlayout.css" type="text/css">
<link rel="stylesheet" href="css/print.css" type="text/css">
<meta content="text/html; charset=gb2312" http-equiv="content-type">
<title>简介类别(Class)</title>
</head>
<body>
<h3><a href="http://caterpillar.onlyfun.net/GossipCN/index.html">From
Gossip@caterpillar</a></h3>
<h1><a href="CppGossip.html">C++
Gossip: 简介类别(Class)</a></h1>
class是C++中用来封装资料的关键字,当您使用类别来定义一个物件(Object)时,您考虑这个物件可能拥有的“属性”(Property)与
“方法”(Method),属性是物件的静态描述,而方法是可施加于物件上的动态操作,您使用类别定义出这个物件的规格书,之后就可依这个规格书制作出一
个个的物件实例,并在制作过程中设定个别物件的专属特性资料。 <br>
<br>
举个例子来说,您可以定义一个“球”的模子,考虑球有各种不同的颜色(或名称),以及球最基本的球半径资讯,您想到这些资讯应该可以取得,并可以进一步取
得球的体积,当您在C++中要包装这些资讯时,您可以如下进行定义: <br>
<ul>
<li>Ball.h</li>
</ul>
<pre>#include <string><br>using namespace std;<br><br>class Ball { <br>public: <br> Ball(); <br> Ball(double, const char*); <br> Ball(double, string&); <br> <br> double radius();<br> string& name(); <br> <br> void radius(double); <br> void name(const char*); <br> void name(string&); <br> <br> double volumn(); <br><br>private:<br> double _radius; // 半径 <br> string _name; // 名称 <br>};</pre>
<br>
您在表头档案中定义类别,表头档案的名称建议与类别名称同名,一个定义良好的类别,即使在不看程式码实作的情况下,也可以从定义中看出这个类别的大致功
能;class是C++中用来定义类别的关键字,Ball是我们取得类别名称,记得一个类别的定义是这么作的: <br>
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">class Ball { </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;"> //
成员定义</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">};</span><br>
</div>
<br>
最重要的是别忘了在最后加上分号,初学C++的新手很常犯这个错误;接下来定义类别的成员,注意到public这个关键字,它表示以下所定义的成员可以使
用物件名称直接被呼叫,也称之为“公用成员”或“公开成员”,private关键字下的则是“私用成员”或“私有成员”,不可以透过物件名称直接呼叫。<br>
<br>
在类别封装时,有一个基本原则是:资讯的最小化公开。如果属性可以不公开就不公开,如果要取得或设定物件的某些属性,也是尽量透过方法成员来进行。 <br>
<br>
资讯的最小化公开原则是基于安全性的考量,避免程式设计人员随意操作属性成员而造成程式的错误,您可以在日后的程式设计中慢慢来体会;在稍后的实作中,您
将可以看到,我将不会radius与name两个私用成员直接进行存取,而是透过公开的方法来进行设定。 <br>
<br>
接下来实作类别的内容: <br>
<ul>
<li>Ball.cpp</li>
</ul>
<pre>#include <string><br>#include "Ball.h"<br>using namespace std;<br><br>// 预设建构函式<br>Ball::Ball() {<br> _radius = 0.0; <br> _name = "noname ball"; <br>}<br><br>Ball::Ball(double radius, const char *name) { <br> _radius = radius; <br> _name = name;<br>}<br><br>Ball::Ball(double radius, string &name) { <br> _radius = radius; <br> _name = name;<br>}<br><br>double Ball::radius() { <br> return _radius; <br>} <br><br>double Ball::volumn() { <br> return (4 / 3 * 3.14159 * _radius * _radius * _radius); <br>} <br><br>string& Ball::name() { <br> return _name; <br>} <br><br>void Ball::radius(double radius) { <br> _radius = radius; <br>} <br><br>void Ball::name(string &name) { <br> _name = name; <br>}<br><br>void Ball::name(const char *name) { <br> _name = name; <br>}</pre>
<br>
类别的实作档案通常与类别名称同名,如此例中的Ball.cpp,与类别名称同名的方法称之为“建构函式”(Constructor),也有人称之为“建
构子”,它没有传回值,建构函式的作用在于物件生成时自动初始一些必要的资讯,它可以被过载,以满足物件生成时不同的设定条件。 <br>
<br>
“::”称之为类别范围解析(Class scope
resolution)运算子,在实作类别方法时,在::之前指明您要实作的是哪一个类别的方法,您在实作中过载了建构函式,在不指定引数的情况下,会将
radius设定为0,而name设定为"noname
ball",另两个建构函式则可以指定引数,无参数数建构函式是预设建构函式,如果您没有定义预设建构函式,则编译器会自动帮您产生一个无实作内容的预设
建构函式。<br>
<br>
定义好类别之后,您就可使用这个类别来建立物件,例如: <br>
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">Ball ball1; </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">Ball ball2(5.0,
"black ball");</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">string
name("yellow ball");</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">Ball ball3(10.0,
name);</span><br>
</div>
<br>
也可以这么建立物件:<br>
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">Ball ball1 = Ball(5.0,
"black ball");</span><br>
</div>
<br>
这有些类似宣告变数,使用类别建立的变数称其为“物件”(Object)或“实例”(Instant),在上例中就是ball1、ball2与ball3
三个物件,ball1物件在建立时并不指定任何参数,所以根据之前实例建构函式的内容,b1的radius将设定为0.0,name设定为"noname
ball";ball2则给定两个参数,所以ball2的radius设定为5.0,而ball2的name设定为"black
ball";ball3则是给定radius引数为10.0,第二个参数则给定string实例。 <br>
<br>
您可以透过公开成员来操作物件或取得物件资讯,方法是使用物件名称加上“.”运算子,例如: <br>
<div style="margin-left: 40px; font-family: Courier New,Courier,monospace;"><span style="font-weight: bold;">ball1.name("green
ball");</span><br>
</div>
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">cout <<
ball1.name() << endl;</span><br>
<br>
</div>
以下是使用Ball类别的一个实际例子:<br>
<ul>
<li>main.cpp</li>
</ul>
<pre>#include <iostream> <br>#include "Ball.h"<br>using namespace std; <br><br>int main() {<br> Ball ball1;<br> cout << ball1.name() << "\t"<br> << ball1.volumn() <br> << endl;<br> <br> ball1.name("green ball");<br> ball1.radius(2.5);<br> cout << ball1.name() << "\t"<br> << ball1.volumn() <br> << endl;<br><br> Ball ball2(5.0, "black ball");<br> cout << ball2.name() << "\t"<br> << ball2.volumn() <br> << endl; <br> <br> string name("yellow ball");<br> Ball ball3(10.0, name);<br> <br> cout << ball3.name() << "\t"<br> << ball3.volumn() <br> << endl;<br> <br> return 0; <br>}<br></pre>
<br>
<span class="postbody">
执行结果:</span><br>
<table style="text-align: left; width: 100%;" border="0" cellpadding="2" cellspacing="2">
<tbody>
<tr>
<td style="background-color: rgb(0, 0, 0);"><small><span style="color: rgb(255, 255, 255);">noname ball
0<br>
green ball 49.0873<br>
black ball 392.699<br>
yellow ball 3141.59</span></small><span style="color: rgb(255, 255, 255);"><br>
</span></td>
</tr>
</tbody>
</table>
<br>
对于简单的成员函式,您可以将之实作于类别定义中,在类别定义中即实作的函式会自动成为inline函式,例如:<br>
<ul>
<li>Ball.h</li>
</ul>
<pre>#include <string><br>using namespace std;<br><br>class Ball { <br>public: <br> Ball(); <br> Ball(double, const char*); <br> Ball(double, string&); <br> <br> // 实作于类别定义中的函式会自动inline<br> double radius() {<br> return _radius;<br> }<br> <br> string& name() {<br> return _name; <br> }<br> <br> void radius(double radius) {<br> _radius = radius;<br> } <br> <br> void name(const char *name) {<br> _name = name;<br> }<br> <br> void name(string& name) {<br> _name = name;<br> }<br> <br> double volumn() {<br> return (4 / 3 * 3.14159 * _radius * _radius * _radius); <br> }<br> <br>private:<br> double _radius; // 半径 <br> string _name; // 名称 <br>};</pre>
<br>
<ul>
<li>Ball.cpp</li>
</ul>
<pre>#include <string><br>#include "Ball.h"<br>using namespace std;<br><br>// 预设建构函式<br>Ball::Ball() {<br> _radius = 0.0; <br> _name = "noname ball"; <br>}<br><br>Ball::Ball(double radius, const char *name) { <br> _radius = radius; <br> _name = name;<br>}<br><br>Ball::Ball(double radius, string &name) { <br> _radius = radius; <br> _name = name;<br>}</pre>
<br>
在定义类别时,如果您只是需要使用到某个类别来宣告指标或是参考,但不涉及类别的生成或操作等讯息,则您可以作该类别的前置宣告(Forward
declaration),而不用含入该类别的定义,例如:<br>
<ul>
<li>Test.h</li>
</ul>
<pre>class Ball;<br><br>class Test { <br>public:<br> Test();<br> Test(Ball*); <br> <br> Ball* ball(); <br> void ball(Ball*);<br><br>private:<br> Ball *_ball; // 名称 <br>};<br></pre>
<br>
在定义Test类别时,您尚未真正使用Ball来建构物件进行操作,您只是用它来宣告一些名称,则您只要使用前置宣告就可以了,实际实作类别时再含入
Ball.h表头档即可,例如:<br>
<ul>
<li>Test.cpp</li>
</ul>
<pre>#include "Test.h"<br>#include "Ball.h"<br><br>Test::Test() { <br> _ball = new Ball;<br>}<br><br>Test::Test(Ball *ball) {<br> _ball = ball;<br>}<br><br>Ball* Test::ball() {<br> return _ball;<br>}<br><br>void Test::ball(Ball *ball) {<br> _ball = ball; <br>}<br></pre>
<br>
如果您的类别定义有单一参数的建构函式(或除了第一个参数之外,其它参数都有预设值的建构函式),则预设会有自动转换的作用,例如:<br>
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">class Ball { </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">public: </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">
Ball(const char*); </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;"> ...</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">};</span><br>
</div>
<br>
则您可以使用以下的方式来建构物件并初始化:<br>
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">Ball ball = "Green
ball";</span><br>
</div>
<br>
预设的转换行为是由编译器施行的,但有时是有危险的,如果您不希望编译器自作主张,则您可以使用explicit修饰,告诉编译器不要自作主张:<br>
<div style="margin-left: 40px;"><span style="font-weight: bold; font-family: Courier New,Courier,monospace;">class Ball { </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">public: </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">
explicit Ball(const char*); </span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;"> ...</span><br style="font-weight: bold; font-family: Courier New,Courier,monospace;">
<span style="font-weight: bold; font-family: Courier New,Courier,monospace;">};</span><br>
<span style="font-weight: bold; font-style: italic;"></span></div>
<span style="font-weight: bold; font-style: italic;"><br>
<br>
</span><br>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -