-
a是int型變量a的地址
(char *)將int型指針(指向4個字節)轉換成char型指針(指向一個字節)
char *p聲明一個char型指針變回量,答接受轉換的地址。
#include <stdio.h>
int main()
{
? ?int i = 0x1122;
? ?char * p = (char *)&i;
? ?if (p[0] == 0x22 && p[1] == 0x11) {
? ? ? ?printf("Little Endian\n");
? ?}
? ?else if (p[0] == 0x11 && p[1] == 0x22) {
? ? ? ?printf("Big Endian\n");
? ?}
}查看全部 -
計算機硬件有兩種儲存數據的方式:大端字節序(big endian)和小端字節序(little endian)。
如果一個使用小端字節序的電腦上,這個整數的高字節就會存放在高地址上:
現在大部分的機器,都采用了小端字節序。但是在 IO 方面,則大部分使用大端字節序。例如,你要使用網絡發送一個 int 類型的變量,要先把 int 轉換成大端字節序,然后通過網絡發送。
大端字節序又被稱之為網絡細節序。
查看全部 -
^ 異或
若參加運算的兩個二進制位值相同則為0,否則為1。
<< 左移
各位全部左移若干位,高位丟棄,低位補 0 。
>> 右移
各二進位全部右移若干位,對無符號數,高位補 0 ,有符號數,各編譯器處理方法不一樣,有的補符號位,有的補 0 。
查看全部 -
你知道大端字節序和小端字節序嗎?
字節序,就是 大于一個字節類型的數據在內存中的存放順序。
計算機硬件有兩種儲存數據的方式:大端字節序(big endian)和小端字節序(little endian)。
我們現在有一個整數是258。用16進制表示是0x0102,然后我們把這個整數拆分成兩個字節,第一個字節為 0000 0001,第二個字節為 0000 0010。
如果在一個使用大端字節序的電腦上,這個整數會被這樣存放:
如果一個使用小端字節序的電腦上,這個整數的高字節就會存放在高地址上:
現在大部分的機器,都采用了小端字節序。但是在 IO 方面,則大部分使用大端字節序。例如,你要使用網絡發送一個 int 類型的變量,要先把 int 轉換成大端字節序,然后通過網絡發送。
大端字節序又被稱之為網絡細節序。
查看全部 -
不一樣的const關鍵字
C++ 中的 const 千變萬化,之前我們已經學過使用 const 來做一個常量。const 在 C++ 中整體表示的語意是“不變的”,但是 const 申明在不同位置,卻會有不一樣的效果。這一小節,我們來集中學習一下 const。
const 修飾普通變量
例如:
const int a = 20;
則表示 a 是一個常量,你不可以在后續對其進行修改。因為 a 不可修改,所以在創建的時候就要對 a 進行賦值,不對其進行賦值則會報錯。例如,下面的代碼就會報錯
const int a;
const 修飾指針
const 修飾指針可以分為多種情況:
只有一個 const,如果 const 位*左側,表示指針所指數據是常量,不能通過解引用修改該數據;指針本身是變量,可以指向其他的內存單元
int aaa = 20; int bbb = 30; const int * constPoint = &aaa; constPoint = &bbb; *constPoint = 80; // 這行代碼會報錯
只有一個 const,如果 const 位于*右側,表示指針本身是常量,不能指向其他內存地址;指針所指的數據可以通過解引用修改
int aaa = 20; int bbb = 30; int * const constPoint = &aaa; constPoint = &bbb; // 這行代碼會報錯 *constPoint = 80;
兩個 const,*左右各一個,表示指針和指針所指數據都不能修改
int aaa = 20; int bbb = 30; const int * const constPoint = &aaa; constPoint = &bbb; // 這行代碼會報錯 *constPoint = 80; // 這行代碼會報錯
const 修飾函數參數
const 修飾函數參數和修飾普通函數是一樣的。但是要注意的時候 const 修飾函數參數的時候,其作用域僅僅限制在函數內部。也就是說,你可以把一個不用 const 修飾的參數傳入到 const 修飾的參數中去。而只要在函數中保持其不變性就可以了。
const 修飾成員函數
const 修飾的成員函數不能修改任何的成員變量
class A { public: ? ? int aaa; ? ? int funcA() const ? ? { ? ? ? ? aaa = 20; // 這行代碼會報錯 ? ? ? ? return 0; ? ? } }
const 成員函數不能調用非 const 成員函數
class A { public: ? ? int aaa; ? ? int funcA() const ? ? { ? ? ? ? funcB(); // 這行代碼會報錯 ? ? ? ? return 0; ? ? } ? ? int funcB() ? ? { ? ? ? ? return 0; ? ? } }
const 修飾函數返回值修飾返回值要分成兩種情況
址傳遞,返回指針,引用。
在 C++ 中有時我們會寫這樣的代碼:
A aaa; A & getA(){ ? ? return aaa; } int main(int argc,char **argv) { ? ? A bbb; ? ? getA() = bbb; ? ? return 0; }
上面的代碼運行之后,aaa 變量就會被 bbb 所賦值,但是有些時候這樣做會造成一些混亂
例如:
getA() == a
有時候會有筆誤:
getA() = a
這種情況下,就會有問題,而且不容易被找到這個錯誤。
所以在大多數情況下,我們可以給返回值加一個 const ,這樣可以防止返回值被調用。
A aaa; const A & getA(){ ? ? return aaa; } int main(int argc,char **argv) { ? ? A bbb; ? ? getA() = bbb; // 這行代碼會報錯 ? ? return 0; }
值傳遞。
值傳遞就簡單多了,因為值傳遞的時候,返回值會被復制一份,所以加不加 const 都可以。
查看全部 -
C++ 中的空指針
我們之前的課程中曾經講過空指針的問題,知道在 C++ 中,有使用 NULL 和 nullptr 兩種方式表示空指針的方法。
int * p = NULL; int * p = nullptr;
這一小節來看看兩者的區別。
NULL
首先看 NULL,在 C++ 中,NULL 其實就是 0。
例如:
int * p = NULL;
等價于:
int * p = 0;
因為在 C++ 中,0 地址通常是被保護起來的,不可訪問的。因此用 0 地址來指代這個指針哪里都不指,是可以的。但是這里面卻存在一些問題。因為 NULL 就是 0,所以我們可以把 NULL 用在其他地方。
例如:
int a = NULL;
我們可以將一個 int 變量賦值成 NULL,你永遠無法阻止有人這么干。而在某些情況下,甚至會在不經意間釀成慘劇。
例如:
class A { public: ? ? void func(void * t) ? ? { ? ? } ? ? void func(int i) ? ? { ? ? } }
這個類中,func 函數有兩個重載。這個時候,我們嘗試用 NULL 調用一下:
int main(int argc,char **argv) { ? ? A a; ? ? a.func(NULL); ? ? return 0; }
猜猜這個函數到底調用的哪個重載?
nullptr
正是由于 NULL 會導致這樣的混亂,所以在 C++11 標準之后,C++ 標準委員會為 C++ 添加了 nullptr 關鍵字。我們可以將 NULL 賦值給一個普通變量,而 nullptr 卻不能。
int a = nullptr;
這樣是會直接報錯的。
nullptr 只能賦值給指針,所以不會有 NULL 那樣的問題。
所以,只要你的編譯器兼容 C++11 標準,那么你應該使用 nullptr。
查看全部 -
霸道總裁眼中的命令員工:父類和子類的相互轉換
我們在之前的課程中學習過數據類型之間的轉換,某些可以隱式轉換,某些需要顯式轉換。
例如,我們可以把 int 轉成 long long。
int a = 100; long long b = a;
由于是小類型轉大類型,所以這里使用隱式轉換就可以了。
再比如,我們可以把 long long 轉成 int。
long long a = 100; int b = (int)a;
這里是大類型轉小類型,所以要顯式轉換。這種轉換可能會損失精度,是需要我們程序員掌控的。
類的轉換
同樣,類之間也可以相互轉換。類的轉換主要是在父類和子類之間的轉換。
首先,我們先來看看父類和子類之間的邏輯關系
class Staff { public: ? ? std::string name; ? ? int age; } ?class Coder : public Staff { public: ? ? std::string language; };
這里有兩個類,一個類是 Staff 員工類,里面包括兩個屬性,name 和 age。Coder 程序員類繼承自員工類,所以其包含了 Staff 的屬性,除此之外,還有一個 language 屬性,表示其使用的編程語言。
我們其實可以把一個員工強行轉化成程序員,但是這就有可能出問題,就如同我們把 long long 轉成 int 就有可能出現問題。
int main(int argc,char **argv) { ? ? Coder * coder = new Coder(); ? ? Staff * staff = coder; // 隱式轉換就可以 ? ? Coder * coder = (Coder *)staff; // 必須顯式轉換 ? ? return 0; }
查看全部 -
動態編聯和靜態編聯
上一小節,我們介紹了父類和子類的轉換,也簡單介紹了多態,從這節開始,我們詳細介紹多態。
class Base { public: ? ? void func(){ ? ? ? ? printf("this is Base\n"); ? ? } } class Child : public Base { public: ? ? void func(){ ? ? ? ? printf("this is Child\n"); ? ? } } ?int main(int argc,char **argv) { ? ? Child * obj = new Child(); ? ? Base * baseobj = (Base *)obj; ? ? baseobj->func(); ? ? delete obj; ? ? return 0; }
我們之前講述了什么是多態,還用了一個例子,將一個指針的類型做成強轉,然后調用 func 函數,就會發現, func 函數會隨著被強轉的類型的變換而變換,這種函數的關聯過程稱為編聯。按照聯編所進行的階段不同,可分為兩種不同的聯編方法:靜態聯編和動態聯編。
靜態編聯
Child * obj = new Child(); Base * baseobj = (Base *)obj; baseobj->func(); delete obj; return 0;
再來看看這個例子,我們通過強制轉換來指定 func 執行的是哪個。這個過程是在編譯階段就將函數實現和函數調用關聯起來,因此靜態聯編也叫早綁定,在編譯階段就必須了解所有的函數或模塊執行所需要檢測的信息。
動態編聯
除了靜態編聯之外,C++ 還支持動態編聯。動態聯編是指聯編在程序運行時動態地進行,根據當時的情況來確定調用哪個同名函數,實際上是在運行時虛函數的實現。當然,我們現在所學的知識還沒辦法完成動態編聯,接下來,我們將要學習虛函數,來實現動態編聯。
查看全部 -
引用和指針用法的區別。
查看全部 -
引用和指針功能一樣,引用必須要初始化,賦值nullptr。
int &ra = a;
引用只能指向一個地址,而指針可以變化自己的指向地址。
查看全部 -
純虛函數只可以被繼承。
將父類的析構函數聲明為虛函數,作用是用父類的指針刪除一個派生類對象時,派生類對象的析構函數會被調用。例如:
class Staff
{
public:
? ?std::string name;
? ?int age;
? ?virtual ~Staff()
? ?{
? ?}
}
class Coder : public Staff
{
public:
? ?std::string language;
? ?virtual ~Coder()
? ?{
? ?}
};
int main(int argc,char **argv)
{
? ?Staff * s = new Coder();
? ?delete s;
? ?return 0;
}此時如果析構函數不加 virtual,那么 delete 父類指針的時候,子類的析構就不會被調用,某些情況下會導致內存泄漏。
查看全部 -
純虛函數只可以被繼承。
將父類的析構函數聲明為虛函數,作用是用父類的指針刪除一個派生類對象時,派生類對象的析構函數會被調用。例如:
class Staff
{
public:
? ?std::string name;
? ?int age;
? ?virtual ~Staff()
? ?{
? ?}
}
class Coder : public Staff
{
public:
? ?std::string language;
? ?virtual ~Coder()
? ?{
? ?}
};
int main(int argc,char **argv)
{
? ?Staff * s = new Coder();
? ?delete s;
? ?return 0;
}此時如果析構函數不加 virtual,那么 delete 父類指針的時候,子類的析構就不會被調用,某些情況下會導致內存泄漏。
查看全部 -
給員工分部門:類的繼承
類,就像是對某一類事物的抽象模版,我們可以根據這個模版生產出具有相同屬性的對象。例如,我們之前將員工抽象成了 Staff 類。
而在某些場景下,我們希望對抽象的內容進行擴增,或者說更加具體化。例如,我們之前定義了員工,但是員工只是很抽象的一個概念,員工和員工是不一樣的,例如,程序員和會計都是員工,他們都具有員工應有的屬性,但是除此之外,他們還有額外屬于自己的東西。
為了完成這種關系,我們來學習一下繼承。
例如,我們可以來寫一個程序員類,名字叫做 Coder
class Coder { };
這個 Coder 是員工的一種,他具有員工的所有屬性,所以,我們可以讓 Coder 繼承自 Staff
class Coder : public Staff { };
當然,除了具有 Staff 的所有內容之外,Coder 還有屬于自己的動作,那就是寫代碼,我們就可以這樣寫:
class Coder : public Staff { public: ? ? void code() ? ? { ? ? ? ? printf("Coding!!!\n"); ? ? } };
這樣,Coder 這個類,除了具有 Staff 的所有成員變量和成員函數之外,還有了一個屬于自己的函數。
查看全部 -
面向過程的結構化編程:把數據放入到動作當中。采用自頂向下的方法構建程序,包含順序,選擇和循環三種結構。按照程序執行的時序步驟來完成。
類由成員函數和成員變量組成,可以通過實例化,得出很多的對象。
查看全部 -
運算符重載
RMB.h
class RMB { public: ? ? RMB(int _yuan, int _jiao, int _fen); ? ? ~RMB(); private: ? ? int yuan = 0; ? ? int jiao = 0; ? ? int fen = 0; };
RMB.cpp
#include "RMB.h" RMB::RMB(int _yuan, int _jiao, int _fen) { ? ? yuan = _yuan; ? ? jiao = _jiao; ? ? fen = _fen; } RMB::~RMB() { }
為這個類寫了些必要的部分之后,我們要完成一個功能,加法功能,1塊9毛加2塊3毛,用程序應該怎么寫呢?我們可以添加一個 Add 函數,如下:
RMB.h
class RMB { public: ? ? RMB(int _yuan, int _jiao, int _fen); ? ? ~RMB(); ? ? RMB Add(const RMB & rmb); private: ? ? int yuan = 0; ? ? int jiao = 0; ? ? int fen = 0; };
RMB.cpp
#include "RMB.h" RMB::RMB(int _yuan, int _jiao, int _fen) { ? ? yuan = _yuan; ? ? jiao = _jiao; ? ? fen = _fen; } RMB::~RMB() { } RMB RMB::Add(const RMB & rmb) { ? ? RMB rmbRes(0, 0, 0); ? ? // 分 ? ? int f = rmb.fen + fen; ? ? int f_ = f / 10; ? ? rmbRes.fen = f % 10; ? ? // 角 ? ? int j = rmb.jiao + jiao + f_; ? ? int j_ = j / 10; ? ? rmbRes.jiao = j % 10; ? ? // 元 ? ? int y = rmb.yuan + yuan + j_; ? ? int y_ = y / 10; ? ? rmbRes.yuan = y % 10; ? ? return rmbRes; }
這樣,我們就實現了一個 Add 函數,如果想要把兩個人民幣加起來,就可以這樣用:
int main(int argc,char **argv) { ? ? RMB rmbA(1, 9, 0); ? ? RMB rmbB(2, 5, 0); ? ? RMB rmbC = rmbA.Add(rmbB); ? ? return 0; }
但是這樣看上去好像有點別扭,事實上,在很多不支持運算符重載的語言里,我們都是這樣干的。但是在 C++ 里,有一種更好的方式,可以把 + 號進行重載。
我們可以把這個 Add 函數修改成 + 號的重載:
RMB.h
class RMB { public: ? ? RMB(int _yuan, int _jiao, int _fen); ? ? ~RMB(); ? ? // RMB & Add(const RMB & rmb); ? ? RMB operator + (const RMB & rmb); private: ? ? int yuan = 0; ? ? int jiao = 0; ? ? int fen = 0; };
RMB.cpp
#include "RMB.h" RMB::RMB(int _yuan, int _jiao, int _fen) { ? ? yuan = _yuan; ? ? jiao = _jiao; ? ? fen = _fen; } RMB::~RMB() { } // RMB & RMB::Add(const RMB & rmb) RMB RMB::operator + (const RMB & rmb) { ? ? RMB rmbRes(0, 0, 0); ? ? // 分 ? ? int f = rmb.fen + fen; ? ? int f_ = f / 10; ? ? rmbRes.fen = f % 10; ? ? // 角 ? ? int j = rmb.jiao + jiao + f_; ? ? int j_ = j / 10; ? ? rmbRes.jiao = j % 10; ? ? // 元 ? ? int y = rmb.yuan + yuan + j_; ? ? int y_ = y / 10; ? ? rmbRes.yuan = y % 10; ? ? return rmbRes; }
在這樣修改之后,使用的時候,我們就可以寫出來更優雅的代碼了。
int main(int argc,char **argv) { ? ? RMB rmbA(1, 9, 0); ? ? RMB rmbB(2, 5, 0); ? ? RMB rmbC = rmbA + rmbB; ? ? return 0; }
可以看到我們直接把兩個對象用 + 號加了起來,這比之前使用函數看起來會好得多。
在 C++ 中,有很多符號都可以重載,也有一些不能被重載。
查看全部 -
類
類,是 C++ 實現面向對象最基礎的部分。類其實和之前學過的結構體十分相似,你可以認為類是結構體的升級版。
類的申明
在 C++ 中,可以用下面的代碼申明一個員工類:
class Staff { };
可以像使用結構體一樣使用這個類:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) {? ? ?Staff st;? ? ?return 0; }
分文件編程
我們在此之前都是把代碼放到一個文件里,但是這樣在實際工程中肯定是不行的,我們不可能把所有的代碼都寫到一個文件夾里面。而在 C++ 中我們就常常把類定義到不同的文件里面,把每個類都獨立起來,這樣代碼的耦合性就會降低,方便維護。
在 C++ 中,我們可以把一個類寫到兩個文件里面,一個是后綴為 .h 或者 .hpp 的頭文件,一個是后綴為 .cpp 的實現文件。我們先在開發環境里新建一個類。輸入類名是 Staff。
可以看到 VS 為我們創建類兩個文件,Staff.h 和 Staff.cpp。Staff.h 文件為定義,Staff.cpp 為實現。
在分了文件之后,我們想要在 main 函數中引用這個類,就需要使用 #include “Staff.h” 將頭文件引入進來。
實例化
在新建了一個類之后,我們就可以根據這個類產生對象了。根據類產生對象的過程叫做實例化。
#include "Staff.h"?
int main(int argc,char **argv)?
{? ? ?// 我們就這樣實例化了三個員工? ? ?
Staff st1;? ? ?
Staff st2;? ? ?
Staff st3;? ??
return 0;?
}
這樣分配,我們將這三個“員工”分配到了棧上,同樣的,可以把他們分配到堆內存上面去。
new delete
要將對象分配到堆上,需要用到另外兩個關鍵字,new 和 delete。new 用來分配對象,delete 用來刪除對象。new 會返回一個指針,在使用完畢后,要通過 delete 把這個指針指向的地址釋放掉。
#include "Staff.h"?
int main(int argc,char **argv) {? ??
Staff * st1 = new Staff();? ? ?
Staff * st2 = new Staff();? ? ?
Staff * st3 = new Staff();? ? ?
// 記得釋放? ??
?delete st1;? ? ?
delete st2;? ? ?
delete st3;? ? ?
return 0;
?}
查看全部 -
霸道總裁的員工:類
我們上一小節中介紹了面向對象的思想,這一小節開始,我們來具體看看在 C++ 中應該如何實現面向對象。
類
類,是 C++ 實現面向對象最基礎的部分。類其實和之前學過的結構體十分相似,你可以認為類是結構體的升級版。
類的申明
在 C++ 中,可以用下面的代碼申明一個員工類:
class Staff { };
可以像使用結構體一樣使用這個類:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) {? ? ?Staff st;? ? ?return 0; }
分文件編程
我們在此之前都是把代碼放到一個文件里,但是這樣在實際工程中肯定是不行的,我們不可能把所有的代碼都寫到一個文件夾里面。而在 C++ 中我們就常常把類定義到不同的文件里面,把每個類都獨立起來,這樣代碼的耦合性就會降低,方便維護。
在 C++ 中,我們可以把一個類寫到兩個文件里面,一個是后綴為 .h 或者 .hpp 的頭文件,一個是后綴為 .cpp 的實現文件。我們先在開發環境里新建一個類。輸入類名是 Staff。
可以看到 VS 為我們創建類兩個文件,Staff.h 和 Staff.cpp。Staff.h 文件為定義,Staff.cpp 為實現。
在分了文件之后,我們想要在 main 函數中引用這個類,就需要使用 #include “Staff.h” 將頭文件引入進來。
實例化
在新建了一個類之后,我們就可以根據這個類產生對象了。根據類產生對象的過程叫做實例化。
#include "Staff.h"?
int main(int argc,char **argv)?
{? ? ?// 我們就這樣實例化了三個員工? ? ?
Staff st1;? ? ?
Staff st2;? ? ?
Staff st3;? ??
return 0;?
? ??
}
這樣分配,我們將這三個“員工”分配到了棧上,同樣的,可以把他們分配到堆內存上面去。
new delete
要將對象分配到堆上,需要用到另外兩個關鍵字,new 和 delete。new 用來分配對象,delete 用來刪除對象。new 會返回一個指針,在使用完畢后,要通過 delete 把這個指針指向的地址釋放掉。
#include "Staff.h"?
int main(int argc,char **argv) {? ??
Staff * st1 = new Staff();? ? ?
Staff * st2 = new Staff();? ? ?
Staff * st3 = new Staff();? ? ?
// 記得釋放? ??
?delete st1;? ? ?
delete st2;? ? ?
delete st3;? ? ?
return 0;
?}
查看全部 -
霸道總裁的員工:類
我們上一小節中介紹了面向對象的思想,這一小節開始,我們來具體看看在 C++ 中應該如何實現面向對象。
類
類,是 C++ 實現面向對象最基礎的部分。類其實和之前學過的結構體十分相似,你可以認為類是結構體的升級版。
類的申明
在 C++ 中,可以用下面的代碼申明一個員工類:
class Staff { };
可以像使用結構體一樣使用這個類:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) { ? ? Staff st; ? ? return 0; }
分文件編程
我們在此之前都是把代碼放到一個文件里,但是這樣在實際工程中肯定是不行的,我們不可能把所有的代碼都寫到一個文件夾里面。而在 C++ 中我們就常常把類定義到不同的文件里面,把每個類都獨立起來,這樣代碼的耦合性就會降低,方便維護。
在 C++ 中,我們可以把一個類寫到兩個文件里面,一個是后綴為 .h 或者 .hpp 的頭文件,一個是后綴為 .cpp 的實現文件。我們先在開發環境里新建一個類。輸入類名是 Staff。
可以看到 VS 為我們創建類兩個文件,Staff.h 和 Staff.cpp。Staff.h 文件為定義,Staff.cpp 為實現。
在分了文件之后,我們想要在 main 函數中引用這個類,就需要使用 #include “Staff.h” 將頭文件引入進來。
實例化
在新建了一個類之后,我們就可以根據這個類產生對象了。根據類產生對象的過程叫做實例化。
#include "Staff.h" int main(int argc,char **argv) { ? ? // 我們就這樣實例化了三個員工 ? ? Staff st1; ? ? Staff st2; ? ? Staff st3; ? ? return 0; }
這樣分配,我們將這三個“員工”分配到了棧上,同樣的,可以把他們分配到堆內存上面去。
new delete
要將對象分配到堆上,需要用到另外兩個關鍵字,new 和 delete。new 用來分配對象,delete 用來刪除對象。new 會返回一個指針,在使用完畢后,要通過 delete 把這個指針指向的地址釋放掉。
#include "Staff.h"?
int main(int argc,char **argv) {? ??
Staff * st1 = new Staff(); ? ??
Staff * st2 = new Staff(); ? ??
Staff * st3 = new Staff(); ? ??
// 記得釋放 ? ?
?delete st1; ? ??
delete st2; ? ??
delete st3; ? ??
return 0;
?}
查看全部 -
霸道總裁的員工:類
我們上一小節中介紹了面向對象的思想,這一小節開始,我們來具體看看在 C++ 中應該如何實現面向對象。
類
類,是 C++ 實現面向對象最基礎的部分。類其實和之前學過的結構體十分相似,你可以認為類是結構體的升級版。
類的申明
在 C++ 中,可以用下面的代碼申明一個員工類:
class Staff { };
可以像使用結構體一樣使用這個類:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) { ? ? Staff st; ? ? return 0; }
分文件編程
我們在此之前都是把代碼放到一個文件里,但是這樣在實際工程中肯定是不行的,我們不可能把所有的代碼都寫到一個文件夾里面。而在 C++ 中我們就常常把類定義到不同的文件里面,把每個類都獨立起來,這樣代碼的耦合性就會降低,方便維護。
在 C++ 中,我們可以把一個類寫到兩個文件里面,一個是后綴為 .h 或者 .hpp 的頭文件,一個是后綴為 .cpp 的實現文件。我們先在開發環境里新建一個類。輸入類名是 Staff。
可以看到 VS 為我們創建類兩個文件,Staff.h 和 Staff.cpp。Staff.h 文件為定義,Staff.cpp 為實現。
在分了文件之后,我們想要在 main 函數中引用這個類,就需要使用 #include “Staff.h” 將頭文件引入進來。
實例化
在新建了一個類之后,我們就可以根據這個類產生對象了。根據類產生對象的過程叫做實例化。這個過程就像是公司招聘員工一樣,幸運的是,我們作為程序的老板,并不需要和現實中一樣去張貼招聘啟示。在 C++ 中,“招聘“員工,只需要用以下的代碼就可以了。
#include "Staff.h" int main(int argc,char **argv) { ? ? // 我們就這樣實例化了三個員工 ? ? Staff st1; ? ? Staff st2; ? ? Staff st3; ? ? return 0; }
這樣分配,我們將這三個“員工”分配到了棧上,同樣的,可以把他們分配到堆內存上面去。
new delete
要將對象分配到堆上,需要用到另外兩個關鍵字,new 和 delete。new 用來分配對象,delete 用來刪除對象。new 會返回一個指針,在使用完畢后,要通過 delete 把這個指針指向的地址釋放掉。
#include "Staff.h" int main(int argc,char **argv) { ? ? // 我們就這樣實例化了三個員工 ? ? Staff * st1 = new Staff(); ? ? Staff * st2 = new Staff(); ? ? Staff * st3 = new Staff(); ? ? // 記得釋放 ? ? delete st1; ? ? delete st2; ? ? delete st3; ? ? return 0; }
查看全部 -
霸道總裁的員工:類
我們上一小節中介紹了面向對象的思想,這一小節開始,我們來具體看看在 C++ 中應該如何實現面向對象。
類
類,是 C++ 實現面向對象最基礎的部分。類其實和之前學過的結構體十分相似,你可以認為類是結構體的升級版。之后的學習中你會更加理解類?,F在,我們只簡單得來介紹一下類。
類的申明
在 C++ 中,可以用下面的代碼申明一個員工類:
class Staff { };
可以像使用結構體一樣使用這個類:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) { ? ? Staff st; ? ? return 0; }
分文件編程
我們在此之前都是把代碼放到一個文件里,但是這樣在實際工程中肯定是不行的,我們不可能把所有的代碼都寫到一個文件夾里面。而在 C++ 中我們就常常把類定義到不同的文件里面,把每個類都獨立起來,這樣代碼的耦合性就會降低,方便維護。
在 C++ 中,我們可以把一個類寫到兩個文件里面,一個是后綴為 .h 或者 .hpp 的頭文件,一個是后綴為 .cpp 的實現文件。我們先在開發環境里新建一個類。輸入類名是 Staff。
可以看到 VS 為我們創建類兩個文件,Staff.h 和 Staff.cpp。Staff.h 文件為定義,Staff.cpp 為實現。
在分了文件之后,我們想要在 main 函數中引用這個類,就需要使用 #include “Staff.h” 將頭文件引入進來。
實例化
在新建了一個類之后,我們就可以根據這個類產生對象了。根據類產生對象的過程叫做實例化。這個過程就像是公司招聘員工一樣,幸運的是,我們作為程序的老板,并不需要和現實中一樣去張貼招聘啟示。在 C++ 中,“招聘“員工,只需要用以下的代碼就可以了。
#include "Staff.h" int main(int argc,char **argv) { ? ? // 我們就這樣實例化了三個員工 ? ? Staff st1; ? ? Staff st2; ? ? Staff st3; ? ? return 0; }
這樣分配,我們將這三個“員工”分配到了棧上,同樣的,可以把他們分配到堆內存上面去。
new delete
要將對象分配到堆上,需要用到另外兩個關鍵字,new 和 delete。new 用來分配對象,delete 用來刪除對象。new 會返回一個指針,在使用完畢后,要通過 delete 把這個指針指向的地址釋放掉。
#include "Staff.h" int main(int argc,char **argv) { ? ? // 我們就這樣實例化了三個員工 ? ? Staff * st1 = new Staff(); ? ? Staff * st2 = new Staff(); ? ? Staff * st3 = new Staff(); ? ? // 記得釋放 ? ? delete st1; ? ? delete st2; ? ? delete st3; ? ? return 0; }
查看全部 -
霸道總裁的員工:類
我們上一小節中介紹了面向對象的思想,這一小節開始,我們來具體看看在 C++ 中應該如何實現面向對象。
類
類,是 C++ 實現面向對象最基礎的部分。類其實和之前學過的結構體十分相似,你可以認為類是結構體的升級版。之后的學習中你會更加理解類?,F在,我們只簡單得來介紹一下類。
類的申明
在 C++ 中,可以用下面的代碼申明一個員工類:
class Staff { };
可以像使用結構體一樣使用這個類:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) { ? ? Staff st; ? ? return 0; }
分文件編程
我們在此之前都是把代碼放到一個文件里,但是這樣在實際工程中肯定是不行的,我們不可能把所有的代碼都寫到一個文件夾里面。而在 C++ 中我們就常常把類定義到不同的文件里面,把每個類都獨立起來,這樣代碼的耦合性就會降低,方便維護。
在 C++ 中,我們可以把一個類寫到兩個文件里面,一個是后綴為 .h 或者 .hpp 的頭文件,一個是后綴為 .cpp 的實現文件。我們先在開發環境里新建一個類。輸入類名是 Staff。
可以看到 VS 為我們創建類兩個文件,Staff.h 和 Staff.cpp。Staff.h 文件為定義,Staff.cpp 為實現。
在分了文件之后,我們想要在 main 函數中引用這個類,就需要使用 #include “Staff.h” 將頭文件引入進來。
實例化
在新建了一個類之后,我們就可以根據這個類產生對象了。根據類產生對象的過程叫做實例化。這個過程就像是公司招聘員工一樣,幸運的是,我們作為程序的老板,并不需要和現實中一樣去張貼招聘啟示。在 C++ 中,“招聘“員工,只需要用以下的代碼就可以了。
#include "Staff.h" int main(int argc,char **argv) { ? ? // 我們就這樣實例化了三個員工 ? ? Staff st1; ? ? Staff st2; ? ? Staff st3; ? ? return 0; }
這樣分配,我們將這三個“員工”分配到了棧上,同樣的,可以把他們分配到堆內存上面去。
new delete
要將對象分配到堆上,需要用到另外兩個關鍵字,new 和 delete。new 用來分配對象,delete 用來刪除對象。new 會返回一個指針,在使用完畢后,要通過 delete 把這個指針指向的地址釋放掉。
#include "Staff.h" int main(int argc,char **argv) { ? ? // 我們就這樣實例化了三個員工 ? ? Staff * st1 = new Staff(); ? ? Staff * st2 = new Staff(); ? ? Staff * st3 = new Staff(); ? ? // 記得釋放 ? ? delete st1; ? ? delete st2; ? ? delete st3; ? ? return 0; }
查看全部 -
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
const int arrLength = 20;? ?// 數組長度
double* getStusGrade()
{
? ? // 學生成績數組
? ? double gradeArr[arrLength] = {
? ? 60.5, 62.5, 67.5, 40, 50, 60, 70, 80, 90, 100,
? ? 90.5, 12, 13.5, 84, 65, 76, 87, 88, 99, 20.5
? ? };
? ? // 堆內存上分配160個字節的內存空間 20×8個字節
? ? double* arrP = (double*)malloc(arrLength * sizeof(double));
? ? // 將棧內存上的數組數據添加到堆內存空間中
? ? for (int index = 0; index < arrLength; index++)
? ? {
? ? ? ? arrP[index] = gradeArr[index];
? ? }
? ? return arrP;
}
int main(int argc, char** argv)
{
? ? double* ptr = getStusGrade();
? ? // 輸出學生成績
? ? for (int index = 0; index < arrLength; index++)
? ? {
? ? ? ? std::cout << "學生" << index + 1 << "的數學成績是:" << ptr[index] << std::endl;
? ? }
? ? free(ptr);
? ? ptr = nullptr;
? ? return 0;
}
查看全部 -
指針變量其實和普通變量沒有什么區別,一個函數也是可以正常返回一個指針的。
char * func()
{
? ?char * p = nullptr;
? ?return p;
}
int main(int argc,char **argv)
{
? ?return 0;
}但是我們需要思考的是,什么情況下我們要返回一個指針,返回指針的時候需要我們注意些什么?
通常情況下,我們是希望為函數外提供一片內存,例如,我們可以給函數外面提供一個數組。
int * func()
{
? ?int arr[] = {1, 2, 3, 4};
? ?return arr;
}但是這樣寫得話,程序會崩潰掉。原因是,arr 數組是一個局部變量,在 func 結束之后,其內存就被銷毀掉了。此時在函數外面對其進行操作,自然會出問題。所以,要完成這類操作,我們需要把內存分配到堆內存上面。
int * func()
{
? ?int * arr = (int *)malloc(4 * sizeof(int));
? ?return arr;
}這樣就沒有問題了,當然,既然是分配在了堆內存上,就要記得手動銷毀。
int main(int argc,char **argv)
{
? ?int * p = func();
? ?free(p);
? ?return 0;
}查看全部 -
代碼塊中定義的變量被稱之為局部變量。它們在其他函數的語句中是不可見的,也無法訪問它們。
全局變量是在所有函數體的外部定義的,程序的所有部分都可以使用。全局變量不受作用域的影響,其生命周期一直到程序的結束。
int a = 2;
int main(int argc,char **argv)
{
? ?return 0;
}靜態變量受作用域的影響,其生命周期一直到程序的結束。
例如:
void func()
{
? ?static int a = 0;
}我們可以在函數中申明一個靜態變量。值得注意的是,這個變量的作用域雖然是在函數內,但是他并不會隨著函數結束而被銷毀,它會一直存在到程序的結束。
查看全部
舉報