上一章節:
八、重學C++—動態多態(運行期)-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/147004745?spm=1001.2014.3001.5502
本章節代碼:
cpp/cppClassAndFunc.cpp · CuiQingCheng/cppstudy - 碼云 - 開源中國https://gitee.com/cuiqingcheng/cppstudy/blob/master/cpp/cppClassAndFunc.cpp
一、引言
????????學到這里,會深深被C++的靈活多變所折服,學習C++的過程仿佛遨游在瑰麗多彩的海底世界。類與函數便是其中極為精妙的招式。今天,就讓我們一起重新踏上探索 C++ 類與函數的奇妙之旅,揭開它們神秘的面紗。就像古人云:“溫故而知新,可以為師矣。” 重學 C++ 的類與函數,也能讓我們對編程有全新的感悟。
二、重新認識類:類中成員和特殊類
1、內聯函數:代碼中的“瞬移大師“
????????內聯函數,可以是類中的,也可以是普通函數。在常見的函數調用中,程序需要跳轉去執行函數代碼,執行完再跳轉回來,這中間會消耗一些時間和資源。而內聯函數呢,編譯器會直接將函數代碼嵌入到調用它的地方,因此這里常與宏函數對比。
例如:
/*** 1、內聯函數*/
#include <iostream>// 宏函數
# define ADD(a,b) (a+b)// 內聯函數
inline int add(int& a, int &b)
{return a+b;
}class optData{
public:// 類中內聯函數inline int add(int& a, int &b){return a+b;}
};int main()
{int n = 5;int m = 13;std::cout << "sum1: " << add(n,m) << std::endl;std::cout << "sum2: " << ADD(n,m) << std::endl;optData opt;std::cout << "sum3: " << opt.add(n,m) << std::endl;return 0;
}
宏函數不推薦使用,宏函數在設計初,就存在以下缺點,(1)、類型不安全,無法進行類型檢測(2)、無法調試(3)、無法進行遞歸調用(4)、可讀性差。
使用場景:
(1)、短小且頻繁調用的函數:當函數體代碼量很少(通常不超過 3 - 5 行 ),且在程序中會被頻繁調用時,適合定義為內聯函數。
(2)、?模板函數:簡短的模板函數在編譯時會生成具體的函數實例,更容易被內聯化。內聯后的模板函數能提供更好的性能。
#include <iostream>// 模板內聯函數,返回兩個數中較大的值
template <typename T>
inline T max(T a, T b) {return a > b? a : b;
}int main()
{int n = 5;int m = 13;int iMax = max(n, m);std::cout << "iMax: " << iMax << std::endl;return 0;
}
優點:
????????減少函數調用開銷;提高程序執行效率;增強代碼可讀性;類型安全檢查;相比于宏函數方便可調式。
2、靜態成員:不依賴對象存在,是類中的”常駐大使“
????????靜態成員變量是類的所有對象共享的一個變量,它就像班級里的公共財產,不管哪個同學都可以使用它。
例如:
/*** 1、靜態成員*/#include <iostream>class Student {
public:Student(std::string name) :m_name(name){++m_totalStudents;}~Student() {--m_totalStudents;}// 靜態成員函數static int getTotalStudents() {// ++m_totalStudents; 錯誤靜態成員函數中不能出現普通成員變量,這里僅能操作靜態成員變量;return m_totalStudents;}static std::string getSchoolName(){return m_schoolName;}// 普通成員函數int getStudentsCount(){return getTotalStudents();}
private:std::string m_name;// 靜態成員變量static std::string m_schoolName;static int m_totalStudents;
};std::string Student::m_schoolName = "上海中學";
int Student::m_totalStudents = 0;int main()
{// 靜態成員Student stu1("小明");Student stu2("小紅");std::cout << "學生總數:" << Student::getTotalStudents() << " 學校名:"<< Student::getSchoolName()<<std::endl;std::cout << "小明知道的學生總數:" << stu1.getStudentsCount() << std::endl;return 0;
}
注意:
(1)靜態成員函數只能操作靜態成員變量;
(2)靜態成員屬于整個類共有的,不依賴于類所創建的對象;
(3)普通成員函數可以修改靜態成員變量,因為要注意線程安全;
3、const常量
3.1 、成員變量是常量
一經初始化便不能修改;
3.2、成員函數形參為常量
傳入形參在代碼段內不能修改;
3.3、成員函數返回值為常量
3.4、成員函數,后接const修飾
不能修改成員變量值;
例如:
#include <iostream>
class Student {
public:Student(std::string name) :m_name(name){++m_totalStudents;}Student(std::string name, std::string strlocal) : m_name(name),m_strLocal(strlocal){++m_totalStudents;}~Student() {--m_totalStudents;}// 靜態成員函數static int getTotalStudents() {// ++m_totalStudents; 錯誤靜態成員函數中不能出現普通成員變量,這里僅能操作靜態成員變量;return m_totalStudents;}static std::string getSchoolName(){return m_schoolName;}// 普通成員函數int getStudentsCount(){return getTotalStudents();}std::string getSchoolLocation(){return m_strLocal;}void setSex(const std::string strSex){// 傳入的形參不能修改;m_strSex = strSex;}const std::string getSex(){return m_strSex;}// 不能修改所有成員變量void getStudetCount(int &count) const{count = m_totalStudents;}private:const std::string m_strLocal{"上海"};std::string m_name;std::string m_strSex;// 靜態成員變量static std::string m_schoolName;static int m_totalStudents;
};std::string Student::m_schoolName = "上海中學";
int Student::m_totalStudents = 0;
int main()
{//constStudent stu1("小王");stu1.setSex("男");Student stu2("小麗");stu2.setSex("女");Student stu3("小張", "北京");stu3.setSex("男");int stuCount = 0;stu2.getStudetCount(stuCount);std::cout << "小王學校在哪兒:" << stu1.getSchoolLocation() << " 小王性別:" << stu1.getSex() << std::endl;std::cout << "小麗學校在哪兒:" << stu2.getSchoolLocation() << " 小麗學校學生數量:" << stuCount <<std::endl;std::cout << "小張學校在哪兒:" << stu3.getSchoolLocation() << std::endl;return 0;
}
4、無法實例化對象的類
4.1、抽象類無法實例化對象(略)
4.2、構造函數為私有的
// 構造函數私有
class baseClass {
private:baseClass(){std::cout << "構造函數" <<std::endl;}private:int m_num;
};
5、無法被繼承的類final
class NonInheritable2 final {
public:void print() {std::cout << "This is a final class." << std::endl;}
};
6、delete特殊用法(修飾構造函數)
在 C++ 里,在構造函數后面使用?= delete?是 C++11 引入的特性,其作用是顯式地刪除該構造函數。刪除構造函數后,就無法再使用該構造函數來創建類的對象,這在一些場景下很有用,下面詳細介紹:
6.1、禁止默認構造函數
當你不希望類擁有默認構造函數(即無參數的構造函數)時,可使用?= delete?將其刪除。
#include <iostream>class MyClass {
public:MyClass(int value) : data(value) {}// 刪除默認構造函數MyClass() = delete;
private:int data;
};int main() {// MyClass obj; // 錯誤,默認構造函數已被刪除MyClass obj(42); // 正確,使用帶參數的構造函數return 0;
}
在上述代碼中,MyClass的默認構造函數被刪除,所以不能通過無參數方式構造對象;
6.2、禁止拷貝構造函數和拷貝賦值運算符
若你不希望類的對象被復制,可使用?= delete?刪除拷貝構造函數和拷貝賦值運算符。
#include <iostream>class NonCopyable {
public:NonCopyable() = default;// 刪除拷貝構造函數NonCopyable(const NonCopyable&) = delete; // 刪除拷貝賦值運算符NonCopyable& operator=(const NonCopyable&) = delete;
};int main() {NonCopyable obj1;// NonCopyable obj2 = obj1; // 錯誤,拷貝構造函數已被刪除// NonCopyable obj3;// obj3 = obj1; // 錯誤,拷貝賦值運算符已被刪除return 0;
}
此代碼中,NonCopyable?類的拷貝構造函數和拷貝賦值運算符都被刪除,這就阻止了對象的復制操作。
三、友元
友元就像是類的 “好朋友”,雖然不在類的內部,但卻能訪問類的私有成員。它打破了類的封裝性,但有時候為了提高代碼的靈活性和效率,這種 “破格” 也是必要的。
比如:
#include <iostream>class A {
public:A(int data) : m_privateData(data) {}protected:double m_protectedData{3.88};private:friend void friendFunction(A& a); // 友元函數friend class ClassB; // 友元類private:int m_privateData;
};void friendFunction(A& a) {std::cout << "訪問到私有數據:" << a.m_privateData << std::endl;
}class ClassB {
public:void accessClassAData(A& obj) {// 友元類可以訪問 ClassA 的私有和受保護成員std::cout << "Accessing private data of Class A: " << obj.m_privateData << std::endl;std::cout << "Accessing protected data of Class A: " << obj.m_protectedData << std::endl;}
};int main()
{// 友元A a(10);friendFunction(a);ClassB b;b.accessClassAData(a);
}
在上述代碼中,ClassB?被聲明為?A?的友元類,所以?ClassB?的成員函數?accessClassAData?能夠訪問?A?的私有成員?privateData?和受保護成員?protectedData。
使用場景
(1)、數據共享:當兩個類之間存在緊密的關聯,需要共享數據時,可以使用友元類。
注意事項
(1)、打破封裝性:友元類破壞了類的封裝性,因為它允許外部類訪問本類的私有和受保護成員。過度使用友元類會導致代碼的安全性和可維護性降低,所以應該謹慎使用。
(2)、單向性:友元關系是單向的。
(3)、不具有傳遞性。
四、名字空間
名字空間就像是編程世界里的 “房間分隔器”。當我們的項目越來越大,代碼中的名字(變量名、函數名、類名等)可能會產生沖突,這時候名字空間就能把不同功能的代碼隔離開來。
namespace Math {int add(int a, int b) {return a + b;}
}namespace Utility {int add(int a, int b) {return a * b;}
}int main() {std::cout << "Math 名字空間的加法:" << Math::add(3, 5) << std::endl;std::cout << "Utility 名字空間的加法:" << Utility::add(3, 5) << std::endl;return 0;
}
這里定義了?Math?和?Utility?兩個名字空間,它們都有?add?函數,但功能不同,通過名字空間的限定,我們可以準確調用到想要的函數。
五、總結
????????C++ 的類與函數蘊含著無盡的奧秘,從內聯函數的高效執行,到靜態成員的獨特共享機制,再到各種特殊類和關鍵字的巧妙運用,以及友元和名字空間的神奇功能,每一處都值得我們細細品味。