第十一天(對象和類)

      應該有一個星期沒寫了吧,原因之一是被數據結構實驗,需要編程,費時多。以後多抽時間寫吧。希望編輯完並發表沒有過12點。


2011-10-29(Objects and Classes)
1、類的定義格式一般如下:

class Student
{
private:
    char name[20];
    void addNum();
public:
    void setName(char*);
    char* getName();
    void show();
};
注意最後的分號。這個定義和結構體相像,只是類中,成員默認的訪問控制是private而結構體是public,但爲了加強數據隱藏的概念,通常都顯式地使用private。通常的做法是,將類的定義寫在頭文件中,再用另一個cpp文件定義類中聲明的成員函數。定義函數時,有兩點要了解:
①定義成員函數時要使用作用域操作符(::)來表示函數所屬的類;
②類方法可以訪問類的private
**首先要提得是作用域的概念,即是可訪問的地方。如,靜態內部鏈接性的變量在整個源文件文件都是可訪問的,就說作用域是整個源文件。類的作用域,即是類成員函數體範圍的並集。由於函數中不能定義函數,所以定義函數是一般不在類作用域中,要加作用於操作符來定義函數:
void Student::setName(char* n)
{
    strncpy_s(name,n,19);
}
當然,在類作用域中,不必使用::操作符:
void Student::show()
{
    cout << "Name: " << getName() << endl;
}
函數strncpy_s與strncpy功能一樣,不過微軟認爲後者會有危險,所以改寫了更安全的。這個函數的功能是,如果n的字符數小於等於第三個參數19,不足的用NULL補足,大於則自動截取19個字符賦給name。
**成員函數可以訪問類的私有成員,所以成員函數是類對外的一個接口:
                char* Student::getName(){ return name; }
當然也有私有函數的情況,但這種情況中,類作用域外不能訪問,一般用作內部數據的處理。比如在姓名後面加個學號,在類作用域中可以調用這個函數,而不必每次需要這個操作時都要編寫代碼實現。聽起來很像內聯函數,實際上,它就是內聯函數,定義位於類聲明的函數都會自動成爲內聯函數,如果在類中沒有定義函數體,可以在類的聲明後面定義(前面說過,內聯函數可以應在頭文件中定義)
               inline void Student::addNum(){ std::cout << name << 5 << std::endl; }
當然不能在類中定義了的同時再用inline定義。
2、構造函數(Constructor)。
I、定義。與java類似(或言:java與C++類似),C++有構造函數對類成員初始化,不過與java不同的是,C++將構造函數的聲明和定義分開了,即:
//... ...
public:
    Student(char* );
//... ...
Student::Student(char* m_name)
{
    strncpy_s(name, m_name, 19);
}
**規則也差不多:當且僅當沒有顯式定義構造,編譯器自動添加默認構造函數。如果顯式定義了構造函數,則不能在使用默認構造函數了,如在上面的Student的構造定義後,下面的寫法是錯誤的:
                     Student stu; //invalid
如果想這樣寫,必須提供默認構造函數,因爲在C++中有默認參數的概念,所以有下面兩種方法定義默認構造函數:
                     Student();                     //default constructor
                     Student(char* m_name = "Tom"); //default constructor too

II、使用。三種方式:
Student s1 = Student("Karie");    //Form I
Student s2("Janne");              //Form II, the same as Student s2 = Student("Janne");
Student* s3 = new Student("Ace"); //Form III
3、折構函數(Destructior)。顧名思義,是用作清理對象的,在用new分配對象內存的時候用delete來釋放內存,如果不是用new生成對象,只需讓編譯器生成一個隱式的折構函數即可。
**折構函數沒有參數,沒有返回值,爲了與構造函數加以區別,在類名前加上符號“~”,所以Student的折構函數原型必須是:
                              ~Student();
它的函數體一般在類外面定義。
**折構函數不需顯式調用,編譯器會自動調用的(有例外,後面再談)。
4、類對象的一些操作。如果我們將折構函數定義並使用類Student如下:
Student::~Student()
{
    std::cout << "Bye, " << name << std::endl;
}
int main()
{
    Student s = Student("Tom");
    s = Student("Karie");
    s.show();
    Student s1 = Student("Janne");
    s = s1;
    s.show();
    s1.show();
    return 0;
}
將有如下輸出:
                    Bye, Karie
                    Name: Karie
                    Name: Janne
                    Name: Janne
                    Bye, Janne
                    Bye, Janne

①可用構造函數對已有對象進行覆蓋;
②可進行對象的複製;
③上面兩中操作將產生臨時對象,編譯器會使用折構函數清理。
5、const成員函數。如果這樣做:
                    const Student stu = Student("Tom");
                    s.show();

編譯器將報錯,因爲show()函數保證不了對象不被修改,雖然我們知道這個函數是不會修改對象的,但是編譯器不知道,所以要使用關鍵字const來做出保證:
                    void show() const;                     //function prototype
                    void Student::show()const{/*... ...*/} //function defined

這麼聲明後,show()將不能有一切不確定的可能改變對象的操作,比如,show()將不能調用非const成員函數。
6、this指針。this指針實際是所屬類的const型成員指針(比如這裏,this指針爲:Student* const this),指向的是對象本身。比如如果比較兩個對象名字字符串大小,並返回較大的,可以這樣寫:
Student Student::com(Student s)const
{
    if(strcmp(this->name,s.name) >= 0)return *this;
    else return s;
}
調用時,可以用如下兩種形式:
const Student s1("Tom");
const Student s2("Som");
const Student top = s1.com(s2); //Form I
const Student top = s2.com(s1); //Form II
top.show();
7、對象數組。如果我們這樣寫:
                    Student stu[4]; //implicit initialization
在程序沒有顯式地定義默認構造函數時,編譯器會使用函數體爲空的隱式默認構造函數對其初始化。也可以在聲明數組是顯式初始化:
Student stu[4] = {
    Student("Tom"),
    Student() //using default constructor
    //... ...
};

今天筆記好像有點短,編個習題答案:

**定義類Country,包括國家名、人口、領土面積。使用此類,從讀入的一組國家數據中輸出:①領土最大的國家;②人口最多的國家;③人口密度最大的國家

//flieName: Country.h
#ifndef COUNN_H_
#define COUNN_H_

#include <string>
class Country
{
private:
	std::string name;
	int population; //Unit:people
	double area;	//Unit:square kilometer
	double popuDensity()const{return population / area;}
public:
	Country();
	Country(std::string, int, double);
	void show()const;
	Country areaCompare(const Country)const;
	Country popuCompare(const Country)const;
	Country pdCompare(const Country)const;
};
#endif

//filenName: Country.cpp
#include <iostream>
#include "Country.h"

using namespace std;
Country::Country(string na, int popu, double ar)
{
	name = na;
	population = popu;
	area = ar;
}

Country Country::areaCompare(const Country c)const
{
	if(this->area > c.area)return *this;
	else return c;
}

Country Country::popuCompare(const Country c)const
{
	if(this->population > c.population)return *this;
	else return c;
}

Country Country::pdCompare(const Country c)const
{
    double thisPD = this->popuDensity();
    double guestPD = c.popuDensity();
    if(thisPD > guestPD)return *this;
    else  return c;
}

void Country::show()const
{
	cout << "Conutry Name: " << name << endl;
	cout << "Population: " << population << endl;
	cout << "Territory Area: " << area << endl;
	cout << "Population Density: " << popuDensity() << endl;
}
//fileName: Test.cpp
#include <iostream>
#include "Country.h"

using namespace std;
int main()
{
	Country country[4] = {
		Country("China", 1356800000, 9640000),
		Country("Japan", 127767944, 377835),
		Country("Russian", 141000000, 17075500),
		Country("India", 1210200000,2980000)
	};

	Country top = country[0];

	for(int i = 0; i< 4; i++)
		top = top.areaCompare(country[i]);
	cout << "Country of Max-area:\n";
	top.show();
	cout << endl;
	for(int i = 0; i< 4; i++)
		top = top.popuCompare(country[i]);
	cout << "Country of Max-population:\n";
	top.show();
	cout << endl;
	for(int i = 0; i< 4; i++)
		top = top.pdCompare(country[i]);
	cout << "Country of Max-population-density:\n";
	top.show();

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章