C++中 類與物件,類的定義,類的作用域,類中成員,this指標
概要
這篇文章主要內容是關於類與物件,類的定義,類的作用域,類中成員,this指標。寫的比較粗,後期有時間再改。
什麼是類?
對於類,我認為最早的發言人還是亞里士多德。他歸納事物的方法就是這是什麼(屬性)、能幹什麼(方式)、 起個名字(物件名) 、歸類(抽象)。
天地有大美而不言,但人能夠言其美。計算機語言也能在二進位制世界中構建萬事萬物。
那麼C++的想法就是將 事物抽象成一個類,將事物的屬性和方法封裝到類中。用成員變量表示類的屬性,用函式成員表示類的方法。class 為此而生。
class student { private: char _name[20]; char _gender[4]; int _age; public: void SetstudentInfo(char *name, char *gender, int age); void PrintstudentInfo(); };
上面先簡單定義一個student類,學生有姓名,性別,年齡等屬性。學生類還應該有填寫資訊的方法SetstudentInfo();學生類有表示自己資訊的方法 PrintstudentInfo();
歸納一下一個類有自己的屬性(成員變數),自己的方法(函式成員)。
c++是基於面向物件的語言,它具有三大特點:封裝,繼承,多型
這裡主要說明封裝性。 封裝就是將資料和操縱資料的方法有機的結合起來,只留出外部介面進行呼叫。簡單點,我不需要知道電視機上的畫面是怎麼來的,但是我會看電視瞭解新聞資訊就行了。c++在class中加入了訪問限定符 private 和 public。private定義的成員無法被類外訪問,相當於設計者將這些東西藏了起來。public定義的成員可以被類外訪問,相當於提供了使用權。
定義一個類
定義一個類有兩種方式:
1.類的宣告與定義放在一起
2.類的宣告放在.h標頭檔案裡面,類的定義放在.cpp檔案裡面
問題:這兩種定義有什麼區別?
在標頭檔案中放置內部連結的定義卻是合法的,但不推薦使用的。因為標頭檔案被包含到多個原始檔中時,不僅僅會汙染全域性名稱空間,而且會在每個編譯單元中有自己的實體存在。大量消耗記憶體空間,還會影響機器效能。 注1
編譯單元是原始檔的意思。對於第一種方法:,如果在標頭檔案中實現了類的定義,那麼這個標頭檔案無疑是非常大的。這個時候多個原始檔引用了這個標頭檔案就會造成標頭檔案程式碼重複使用。對於第二種方法: 一個單獨的cpp檔案實現了類的方法。這個cpp是能外部連結的。如果多個原始檔引用了這個標頭檔案,那麼他們不需要將標頭檔案的實現拉入自己的檔案中。只需要通過外部連結的方式找到標頭檔案方法的實現就可以了。
class 和 struct 有什麼區別?
1.在struct中無法定義函式成員。如果想要封裝特定的方法就需要使用函式指標。class關鍵字定義出來的類,可以定義函式成員。函式成員可以封裝相應的方法。
2.struct定義出的結構體不具有安全性,它的預設訪問限定符是public。外部程式碼都可以訪問結構體中的變數。class定義出的類具有安全性,它的預設訪問限定符是private。外部程式碼無法訪問被private修飾的成員。
如何在類外訪問私有成員?
1.通過公有的方法
class A{
private :
int a;
public:
int Geta()
{
return a;
}
};
在類中定義一個公有的方法,這個方法可以作用於私有成員變數。
2.指標訪問
class A
{
public:
int a;
private:
int b;
int c;
};
int main()
{
A a;
A *pa = &a;
int *pc = (int *)((int)&a + 4);
*pc = 10;
system("pause");
return 0;
}
假定獲取了一個成員變數的地址,在這個地址上加上相對位置的偏移量就可以修改private 成員。
類的作用域
如何證明定義了一個類,將相當於定義了一個作用域?
初級方案:在類中定義一個變數,在主函式定義一個同名變數能不能呼叫它。
class Test
{
private:
int _t;
};
int main()
{
int _t = 2;
cout << _t << endl;
return 0;
}
列印結果是2,而不是隨機數。以上說明類自成一體,類的範圍就是作用域的範圍。
高階方案:
namespace N
{
int t = 10;
void TestFunc()
{
cout << "TestFunc" << endl;
}
}
class Test
{
public:
void TestFunc(int t)//1.當引數的名字和類中成員變數的名字一樣是誰傳給誰?
{
t = t;
}
void print()
{
cout << t << endl;
}
private:
int t;// 2.宣告為什麼能放在最後面?
};
void TestFunc(int t)
{
cout << "TestFunc(int t)" << endl;
}
int main()
{
int t = 40;
cout << N::t << endl;
Test tt;
tt.TestFunc(20);
tt.print();
system("pause");
return 0;
}
1.當引數的名字和類中成員變數的名字一樣是誰傳給誰?
TestFunc(int t)的引數自己傳給自己。因為引數t在函式的作用域的優先順序最高。在TestFunc(int t)作用域中 int t的優先順序高所以會一直呼叫 本函式的引數進行操作。
2.宣告為什麼能放在最後面?
因為在類中宣告具有全域性屬性,即在一處宣告定義,可以在整個類中處處被使用。類是按照整體來被觀測的,而不是按照C中一步一步被觀測的。
歸結:1.避免函式成員的引數與成員變數同名。2.類中宣告的變數具有全域性屬性
物件的大小
在C語言中由於沒有函式成員,所以按照記憶體對其就可以得到一個struct型別變數的大小。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string.h>
using namespace std;
class student
{
private:
char _name[20];
char _gender[4];
int _age;
public:
void SetstudentInfo(char *name, char *gender, int age);
void PrintstudentInfo();
};
void student::SetstudentInfo(char *name, char *gender, int age)
{
strcpy(_name, name);
strcpy(_gender, gender);
_age = age;
}
int main()
{
student d1;
cout <<"sizeof(d1):"<< sizeof(d1) << endl;
cout << "sizeof(student):" << sizeof(student) << endl;
system("pause");
return 0;
}
在 student 類中 對於成員變數,根據記憶體對其規則它的大小是 28。整個型別的大小也是28,也就是說沒有發現類中成員函式的大小。所以有以下推斷。
類建立的物件時,只建立了物件的成員變數。沒有建立函式成員。多個物件可能公用一個類的函式。
class Test
{};
struct test
{};
int main()
{
Test t;
struct test T;
cout << "sizeof(t):" << sizeof(t) << endl;
cout << "sizeof(T):" << sizeof(T) << endl;
system("pause");
return 0;
}
問題:為什麼空類和空的結構體例項化出的物件 佔了一個位元組?
設定一個空的類和一個空的結構體,測試他們的大小,發現他們兩個的大小都是 1。為什麼對於一個空的物件,或者空的結構體變數的大小是一個1位元組。原因可能是即使沒有屬性的類,它例項出的物件也是有區別的,每個物件 獨一無二 所以要加一個位元組區分。另外如果在記憶體當中建立了一個物件,如果不加一個位元組區分的話,那麼就相當於,多個變數公用一個儲存單元。
this指標
問題:如果呼叫的是同一個函式,那麼編譯器是怎麼知道吧變數放在不同物件的成員變數裡?
class student
{
private:
char _name[20];
char _gender[4];
int _age;
public:
void SetstudentInfo(char *name, char *gender, int age);
void PrintstudentInfo();
};
void student::SetstudentInfo(char *name, char *gender, int age)
{
strcpy(_name, name); // 將 name 放給誰的_name裡,是 d1._name 還是 d2._name 還是d3._name
strcpy(_gender, gender);
_age = age;
}
void student::PrintstudentInfo()
{
cout << _name << " " << _gender << "" << _age << " " << endl;
}
int main()
{
student d1, d2, d3;
d1.SetstudentInfo("John", "man", 17);
d2.SetstudentInfo("Brown", "man", 27);
d3.SetstudentInfo("Mike", "man", 37);
system("pause");
return 0;
}
為什麼編譯器能夠識別不同物件呼叫其公有的類成員函式?原因是編譯器預設,在類成員函式中呼叫了一個 this 指標。this 代表著正在使用這個類成員函式的物件。通過這個this指標可以區別不同物件。
d1.SetstudentInfo("John", "man", 17);
00EC5C38 push 11h
00EC5C3A push 0ECCC7Ch
00EC5C3F push 0ECCC88h
00EC5C44 lea ecx,[d1]
00EC5C47 call student::SetstudentInfo (0EC1230h)
編譯器在呼叫函式的時候先使用了語句 lea ecx,[d1] 將這個物件的地址線儲存起來,然後才呼叫SetstudentInfo函式。
void student::PrintstudentInfo()
//void student::PrintstudentInfo(student *this)
{
cout << this->_name << " " << this->_gender << "" << this->_age << " " << endl;
}
編譯器在編譯過程當中,將類成員函式進行修改,將this新增進入函式。這樣就可以識別不同物件的不同引數。