###C++ 類 & 物件 C++ 在 C 語言的基礎上增加了物件導向程式設計,C++ 支援物件導向程式設計。類是 C++ 的核心特性,通常被稱為使用者定義的型別。 類用於指定物件的形式,它包含了資料表示法和用於處理資料的方法。類中的資料和方法稱為類的成員。函式在一個類中被稱為類的成員。 ####C++ 類定義 定義一個類,本質上是定義一個資料型別的藍圖。這實際上並沒有定義任何資料,但它定義了類的名稱意味著什麼,也就是說,它定義了類的物件包括了什麼,以及可以在這個物件上執行哪些操作。 類定義是以關鍵字 class 開頭,後跟類的名稱。類的主體是包含在一對花括號中。類定義後必須跟著一個分號或一個宣告列表。例如,我們使用關鍵字 class 定義 Box 資料型別,如下所示:
class Box
{
public:
double length; // 盒子的長度
double breadth; // 盒子的寬度
double height; // 盒子的高度
};
複製程式碼
關鍵字 public 確定了類成員的訪問屬性。在類物件作用域內,公共成員在類的外部是可訪問的。您也可以指定類的成員為 private 或 protected。 ####定義 C++ 物件 類提供了物件的藍圖,所以基本上,物件是根據類來建立的。宣告類的物件,就像宣告基本型別的變數一樣。下面的語句宣告瞭類 Box 的兩個物件:
Box Box1; // 宣告 Box1,型別為 Box
Box Box2; // 宣告 Box2,型別為 Box
複製程式碼
物件 Box1 和 Box2 都有它們各自的資料成員。 ####訪問資料成員 類的物件的公共資料成員可以使用直接成員訪問運算子 (.) 來訪問。 ######示例如下:
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 長度
double breadth; // 寬度
double height; // 高度
};
int main()
{
Box Box1; // 宣告 Box1,型別為 Box
Box Box2; // 宣告 Box2,型別為 Box
double volume = 0.0; // 用於儲存體積
// box 1 詳述
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
// box 2 詳述
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;
// box 1 的體積
volume = Box1.height * Box1.length * Box1.breadth;
cout << "Box1 的體積:" << volume << endl;
// box 2 的體積
volume = Box2.height * Box2.length * Box2.breadth;
cout << "Box2 的體積:" << volume << endl;
system("pause");
return 0;
}
結果如下:
Box1 的體積:210
Box2 的體積:1560
複製程式碼
類成員函式
類的成員函式是指那些把定義和原型寫在類定義內部的函式,就像類定義中的其他變數一樣。類成員函式是類的一個成員,它可以操作類的任意物件,可以訪問物件中的所有成員。 讓我們看看之前定義的類 Box,現在我們要使用成員函式來訪問類的成員,而不是直接訪問這些類的成員: ######示例如下:
#include <iostream>
using namespace std;
class Box
{
public:
double length; // 長度
double breadth; // 寬度
double height; // 高度
// 成員函式宣告
double getVolume(void);
void setLength( double len );
void setBreadth( double bre );
void setHeight( double hei );
};
// 成員函式定義
double Box::getVolume(void)
{
return length * breadth * height;
}
void Box::setLength( double len )
{
length = len;
}
void Box::setBreadth( double bre )
{
breadth = bre;
}
void Box::setHeight( double hei )
{
height = hei;
}
// 程式的主函式
int main( )
{
Box Box1; // 宣告 Box1,型別為 Box
Box Box2; // 宣告 Box2,型別為 Box
double volume = 0.0; // 用於儲存體積
// box 1 詳述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// box 2 詳述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// box 1 的體積
volume = Box1.getVolume();
cout << "Box1 的體積:" << volume <<endl;
// box 2 的體積
volume = Box2.getVolume();
cout << "Box2 的體積:" << volume <<endl;
system("pause");
return 0;
}
結果如下:
Box1 的體積: 210
Box2 的體積: 1560
複製程式碼
C++ 類訪問修飾符
資料封裝是物件導向程式設計的一個重要特點,它防止函式直接訪問類型別的內部成員。類成員的訪問限制是通過在類主體內部對各個區域標記 public
、 private
、 protected
來指定的。關鍵字 public
、 private
、 protected
稱為訪問修飾符。
一個類可以有多個 public
、 protected
或 private
標記區域。每個標記區域在下一個標記區域開始之前或者在遇到類主體結束右括號之前都是有效的。成員和類的預設訪問修飾符是 private
。
#####公有(public)成員 公有成員在程式中類的外部是可訪問的。您可以不使用任何成員函式來設定和獲取公有變數的值。
#####私有(private)成員 私有成員變數或函式在類的外部是不可訪問的,甚至是不可檢視的。只有類和友元函式可以訪問私有成員。
#####保護(protected)成員 保護成員變數或函式與私有成員十分相似,但有一點不同,保護成員在派生類(即子類)中是可訪問的。 在下一個章節中,您將學習到派生類和繼承的知識。現在您可以看到下面的例項中,我們從父類 Box 派生了一個子類 smallBox。 下面的例項與前面的例項類似,在這裡 width 成員可被派生類 smallBox 的任何成員函式訪問。 ######示例如下:
#include <iostream>
using namespace std;
class Box
{
protected:
double width;
};
class SmallBox :Box // SmallBox 是派生類
{
public:
void setSmallWidth(double wid);
double getSmallWidth(void);
};
// 子類的成員函式
double SmallBox::getSmallWidth(void)
{
return width;
}
void SmallBox::setSmallWidth(double wid)
{
width = wid;
}
// 程式的主函式
int main()
{
SmallBox box;
// 使用成員函式設定寬度
box.setSmallWidth(5.0);
cout << "Width of box : " << box.getSmallWidth() << endl;
system("pause");
return 0;
}
結果如下:
Width of box : 5
複製程式碼
####繼承中的特點
有public
, protected
,private
三種繼承方式,它們相應地改變了基類成員的訪問屬性。
1.public
繼承:基類public
成員,protected
成員,private
成員的訪問屬性在派生類中分別變成:public
, protected
, private
2.protected
繼承:基類 public
成員,protected
成員,private
成員的訪問屬性在派生類中分別變成:protected
, protected
, private
3.private
繼承:基類public
成員,protected
成員,private
成員的訪問屬性在派生類中分別變成:private
, private
, private
但無論哪種繼承方式,上面兩點都沒有改變:
1.private
成員只能被本類成員(類內)和友元訪問,不能被派生類訪問;
2.protected
成員可以被派生類訪問。
#####public 繼承 ######示例如下:
#include<iostream>
using namespace std;
class A {
public:
int a;
A() {
a1 = 1;
a2 = 2;
a3 = 3;
a = 4;
}
void fun() {
cout << a << endl; //正確
cout << a1 << endl; //正確
cout << a2 << endl; //正確
cout << a3 << endl; //正確
}
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B : public A {
public:
int a;
B(int i) {
A();
a = i;
}
void fun() {
cout << a << endl; //正確,public成員
cout << a1 << endl; //正確,基類的public成員,在派生類中仍是public成員。
cout << a2 << endl; //正確,基類的protected成員,在派生類中仍是protected可以被派生類訪問。
//cout << a3 << endl; //錯誤,基類的private成員不能被派生類訪問。
}
};
int main() {
B b(10);
cout << b.a << endl;
cout << b.a1 << endl; //正確
//cout << b.a2 << endl; //錯誤,類外不能訪問protected成員
//cout << b.a3 << endl; //錯誤,類外不能訪問private成員
system("pause");
return 0;
}
結果如下:
10
1
複製程式碼
#####protected 繼承 ######示例如下:
#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:
int a;
A(){
a1 = 1;
a2 = 2;
a3 = 3;
a = 4;
}
void fun(){
cout << a << endl; //正確
cout << a1 << endl; //正確
cout << a2 << endl; //正確
cout << a3 << endl; //正確
}
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B : protected A{
public:
int a;
B(int i){
A();
a = i;
}
void fun(){
cout << a << endl; //正確,public成員。
cout << a1 << endl; //正確,基類的public成員,在派生類中變成了protected,可以被派生類訪問。
cout << a2 << endl; //正確,基類的protected成員,在派生類中還是protected,可以被派生類訪問。
cout << a3 << endl; //錯誤,基類的private成員不能被派生類訪問。
}
};
int main(){
B b(10);
cout << b.a << endl; //正確。public成員
cout << b.a1 << endl; //錯誤,protected成員不能在類外訪問。
cout << b.a2 << endl; //錯誤,protected成員不能在類外訪問。
cout << b.a3 << endl; //錯誤,private成員不能在類外訪問。
system("pause");
return 0;
}
結果如下:
10
複製程式碼
#####private 繼承 ######示例如下:
#include<iostream>
using namespace std;
class A {
public:
int a;
A() {
a1 = 1;
a2 = 2;
a3 = 3;
a = 4;
}
void fun() {
cout << a << endl; //正確
cout << a1 << endl; //正確
cout << a2 << endl; //正確
cout << a3 << endl; //正確
}
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B : private A {
public:
int a;
B(int i) {
A();
a = i;
}
void fun() {
cout << a << endl; //正確,public成員。
cout << a1 << endl; //正確,基類public成員,在派生類中變成了private,可以被派生類訪問。
cout << a2 << endl; //正確,基類的protected成員,在派生類中變成了private,可以被派生類訪問。
// cout << a3 << endl; //錯誤,基類的private成員不能被派生類訪問。
}
};
int main() {
B b(10);
cout << b.a << endl; //正確。public成員
// cout << b.a1 << endl; //錯誤,private成員不能在類外訪問。
// cout << b.a2 << endl; //錯誤, private成員不能在類外訪問。
// cout << b.a3 << endl; //錯誤,private成員不能在類外訪問。
system("pause");
return 0;
}
結果如下:
10
複製程式碼
####類建構函式 類的建構函式是類的一種特殊的成員函式,它會在每次建立類的新物件時執行。 建構函式的名稱與類的名稱是完全相同的,並且不會返回任何型別,也不會返回 void。建構函式可用於為某些成員變數設定初始值。
#####無參建構函式 ######示例如下:
#include <iostream>
using namespace std;
class Line
{
public:
void setLength(double len);
double getLength(void);
Line(); // 這是建構函式
private:
double length;
};
// 成員函式定義,包括建構函式
Line::Line(void)
{
cout << "Object is being created" << endl;
}
void Line::setLength(double len)
{
length = len;
}
double Line::getLength(void)
{
return length;
}
// 程式的主函式
int main()
{
Line line;
// 設定長度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;
system("pause");
return 0;
}
結果如下:
Object is being created
Length of line : 6
複製程式碼
#####有參建構函式 ######示例如下:
#include <iostream>
using namespace std;
//建構函式、解構函式、拷貝建構函式
class Teacher {
public:
char *name;
int age;
public:
//無參建構函式
Teacher() {
cout << "無參建構函式" << endl;
}
//有參建構函式會覆蓋預設的無參建構函式
Teacher(char * name, int age) {
this->name = name;
this->age = age;
cout << "有參建構函式" << endl;
}
};
void main() {
Teacher t1;
Teacher t2("Rose", 20);
cout << "Teacher name : " << t2.name << endl;
cout << "Teacher age: " << t2.age << endl;
//另外一種呼叫方式
Teacher t3 = Teacher("jack", 21);
cout << "Teacher name : " << t3.name << endl;
cout << "Teacher age: " << t3.age << endl;
system("pause");
}
結果如下:
無參建構函式
有參建構函式
Teacher name : Rose
Teacher age: 20
有參建構函式
Teacher name : jack
Teacher age: 21
複製程式碼
####類的解構函式 類的解構函式是類的一種特殊的成員函式,它會在每次刪除所建立的物件時執行。 解構函式的名稱與類的名稱是完全相同的,只是在前面加了個波浪號 ~ 作為字首,它不會返回任何值,也不能帶有任何引數。解構函式有助於在跳出程式(比如關閉檔案、釋放記憶體等)前釋放資源。 ######示例如下:
#include <iostream>
#define _CRT_SECURE_NO_WARNINGS
using namespace std;
//建構函式、解構函式、拷貝建構函式
class Teacher {
public:
char *name;
int age;
public:
//無參建構函式賦值預設值
Teacher() {
this->name = (char*)malloc(100);
strcpy(name, "jack walson");
age = 20;
cout << "Object is being created" << endl;
}
//解構函式
//當物件被系統釋放時,解構函式被呼叫
//作用:善後處理
~Teacher() {
cout << "Object is being deleted" << endl;
//釋放記憶體
free(this->name);
}
};
void func() {
Teacher t1;
cout << "Teacher name : " << t1.name << endl;
cout << "Teacher age : " << t1.age << endl;
}
void main() {
func();
system("pause");
}
結果如下:
Object is being created
Teacher name : jack walson
Teacher age : 20
Object is being deleted
複製程式碼
####類拷貝建構函式 拷貝建構函式是一種特殊的建構函式,它在建立物件時,是使用同一類中之前建立的物件來初始化新建立的物件。拷貝建構函式通常用於:
- 通過使用另一個同型別的物件來初始化新建立的物件。
- 複製物件把它作為引數傳遞給函式。
- 複製物件,並從函式返回這個物件。 ######淺拷貝 ######示例如下:
//淺拷貝(值拷貝)
#include <iostream>
using namespace std;
//建構函式、解構函式、拷貝建構函式
class Teacher {
private:
char *name;
int age;
public:
//無參建構函式賦值預設值
Teacher(char * name, int age) {
this->name = (char*)malloc(100);
strcpy(this->name, name);
this->age = age;
cout << "Object is being created" << endl;
}
//拷貝建構函式(值拷貝)
//預設拷貝建構函式,就是值拷貝
~Teacher() {
cout << "Object is being deleted" << endl;
//釋放記憶體
free(this->name);
}
void myprintf() {
cout << name << "," << age << endl;
}
};
void func() {
Teacher t1("Jack", 20);
Teacher t2 = t1;
t2.myprintf(); // 錯誤 由於t1解構函式釋放記憶體,t2執行myprintf 為空指標異常
}
void main() {
func();
system("pause");
}
複製程式碼
######深拷貝 ######示例如下:
//深拷貝
#include <iostream>
using namespace std;
class Teacher {
public:
char *name;
int age;
public:
Teacher(char *name, int age) {
int len = strlen(name);
this->name = (char*)malloc(len+1);
strcpy(this->name, name);
this->age = age;
cout << "Object is being created" << endl;
}
~Teacher() {
cout << "Object is being deleted" << endl;
//釋放記憶體
free(this->name);
}
Teacher(const Teacher &obj) {
//複製name屬性
int len = strlen(obj.name);
this->name = (char*)malloc(len+1);
strcpy(this->name, obj.name);
this->age = obj.age;
}
void myprint() {
cout << "Teacher name : " << name << endl;
cout << "Teacher age : " << age << endl;
}
};
void func() {
Teacher t1("Jack", 20);
Teacher t2 = t1;
//由於t1解構函式釋放記憶體,t2執行myprintf 為空指標異常
t2.myprint();
}
void main() {
func();
system("pause");
}
結果如下:
Object is being created
Teacher name : Jack
Teacher age : 20
Object is being deleted
Object is being deleted
複製程式碼
#####結論: ######淺拷貝(值拷貝),拷貝的是指標的地址 ######深拷貝,拷貝的是指標指向的資料內容
友元(友元函式、友元類和友元成員函式)
類的友元函式是定義在類外部,但有權訪問類的所有私有(private)成員和保護(protected)成員。儘管友元函式的原型有在類的定義中出現過,但是友元函式並不是成員函式。
友元可以是一個函式,該函式被稱為友元函式;友元也可以是一個類,該類被稱為友元類,在這種情況下,整個類及其所有成員都是友元。
如果要宣告函式為一個類的友元,需要在類定義中該函式原型前使用關鍵字 friend
#####友元函式
友元函式是指某些雖然不是類成員函式卻能夠訪問類的所有成員的函式。類授予它的友元特別的訪問權,這樣該友元函式就能訪問到類中的所有成員
######示例如下:
#include<iostream>
using namespace std;
//友元函式
class A {
private:
int i;
public:
A(int i) {
this->i = i;
}
void myprint() {
cout << i << endl;
}
//友元函式
friend void modify_i(A*p, int a);
};
//友元函式的實現,在友元函式中可以訪問私有的屬性
void modify_i(A*p, int a) {
p->i = a;
}
void main() {
A* a = new A(10);
a->myprint();
modify_i(a,20);
a->myprint();
system("pause");
}
結果如下:
10
20
複製程式碼
#####友元類 友元類的所有成員函式都是另一個類的友元函式,都可以訪問另一個類中的隱藏資訊(包括私有成員和保護成員)。當希望一個類可以存取另一個類的私有成員時,可以將該類宣告為另一類的友元類。
關於友元類的注意事項:
(1) 友元關係不能被繼承。 (2) 友元關係是單向的,不具有交換性。若類B是類A的友元,類A不一定是類B的友元,要看在類中是否有相應的宣告。 (3) 友元關係不具有傳遞性。若類B是類A的友元,類C是B的友元,類C不一定是類A的友元,同樣要看類中是否有相應的申明。 ######示例如下:
#include<iostream>
using namespace std;
//友元類
class A {
friend class B;
private:
int i;
public:
A(int i) {
this -> i = i;
}
void myprintf() {
cout << i << endl;
}
};
//Java中Class cls = Class.forName("com.test.teacher");
//cls.setAccessable(true); cls相當於com.test.teacher的友元類
class B {
public:
//B這個友元類可以訪問A類的任何成員
void accessAny(A &a) {
a.i = 30;
}
};
void main() {
A a(3) ;
B b;
b.accessAny(a);
a.myprintf();
system("pause");
}
結果如下:
30
複製程式碼
#####友元成員函式 使類B中的成員函式成為類A的友元函式,這樣類B的該成員函式就可以訪問類A的所有成員了。 當用到友元成員函式時,需注意友元宣告和友元定義之間的相互依賴,在該例子中,類B必須先定義,否則類A就不能將一個B的函式指定為友元。然而,只有在定義了類A之後,才能定義類B的該成員函式。更一般的講,必須先定義包含成員函式的類,才能將成員函式設為友元。另一方面,不必預先宣告類和非成員函式來將它們設為友元。 ######示例如下:
#include <iostream>
using namespace std;
class A; //當用到友元成員函式時,需注意友元宣告與友元定義之間的互相依賴。這是類A的宣告
class B
{
public:
void set_show(int x, A &a); //該函式是類A的友元函式
};
class A
{
public:
friend void B::set_show(int x, A &a); //該函式是友元成員函式的宣告
private:
int data;
void show() { cout << data << endl; }
};
void B::set_show(int x, A &a) //只有在定義類A後才能定義該函式,畢竟,它被設為友元是為了訪問類A的成員
{
a.data = x;
cout << a.data << endl;
}
int main(void)
{
class A a;
class B b;
b.set_show(1, a);
system("pause");
return 0;
}
結果如下:
1
複製程式碼
####類的靜態成員 我們可以使用 static 關鍵字來把類成員定義為靜態的。當我們宣告類的成員為靜態時,這意味著無論建立多少個類的物件,靜態成員都只有一個副本。 靜態成員在類的所有物件中是共享的。如果不存在其他的初始化語句,在建立第一個物件時,所有的靜態資料都會被初始化為零。我們不能把靜態成員的初始化放置在類的定義中,但是可以在類的外部通過使用範圍解析運算子 :: 來重新宣告靜態變數從而對它進行初始化 ######示例如下:
//static 靜態關鍵字
#include<iostream>
using namespace std;
//建構函式屬性初始化列表
class Teacher {
public:
char* name;
//計數器
static int total;
public:
Teacher(char* name) {
this->name = name;
cout << "Teacher is being created" << endl;
}
char* setName(char* name) {
return this->name;
}
char* getName() {
return this->name;
}
~Teacher() {
cout << "Teacher is being deleted" << endl;
}
//靜態方法不能呼叫非靜態方法和屬性
static void count() {
total++;
cout << "count:" << Teacher::total << endl;
//cout << this.getName << endl; 報錯靜態變數不能呼叫非靜態方法
}
//非靜態方法可以呼叫靜態方法和屬性
void printf() {
cout << "count:" << Teacher::total << endl;
}
};
//靜態屬性初始化賦值
int Teacher::total = 9;
void main() {
Teacher::total++;
cout << "total:"<< Teacher::total << endl;
//直接通過類名訪問
Teacher::count();
//也可以通過物件名訪問
Teacher t1("John");
t1.count();
system("pause");
}
結果如下:
total:10
count:11
Teacher is being created
count:12
複製程式碼
靜態成員函式與普通成員函式的區別:
- 靜態成員函式沒有 this 指標,只能訪問靜態成員(包括靜態成員變數和靜態成員函式)。
- 普通成員函式有 this 指標,可以訪問類中的任意成員;而靜態成員函式沒有 this 指標。
特別感謝: 菜鳥C++ 教程