?一.友元
C++中使用關鍵字friend可以在類外訪問所有的成員,包括私有成員(之前提到過封裝的核心思想是隱藏內部實現細節,通過公共接口控制訪問),所以友元可以突破封裝的限制訪問數據,盲目使用會導致程序穩定性降低,所以使用友元必須慎重。
友元分類:
????????友元函數
????????友元類
????????友元成員函數
1.友元函數
全局函數訪問私有成員,可以配合運算符重載使用,友元函數是一種在類內“聲明”,類外定義的非成員函數,但是沒訪問類內所有的成員。友元函數可以做到修改私有成員變量
注意:
(1)友元函數沒有this指針。
(2)友元函數可以在類的私有部分,也可以在類的公有部分。
(3)一個函數可以是多個類的友元函數,只需要在不同的類匯中聲明。
#include <iostream>
#include <string>using namespace std;class Techer{
private:int age = 45;string name = "zhangsna";
public:Techer(int age,string name):age(age),name(name){}//Techer():age(45),name("lisi"){}int get_age()const{return age;}string get_name()const{return name;}void print()const{cout << get_age() << endl;cout << get_name() << endl;}int print_age(){return 18;}friend void print_real_age(Techer &t); //類內聲明友元函數
};void print_real_age(Techer &t){ //類外定義友元函數cout << "真實年齡:" << t.age << endl;cout << "虛假年齡:" << t.print_age() << endl;t.age = 30;cout << "修改年齡:" << t.age << endl;
}int main (){Techer t1(34,"zhangsan");cout << t1.print_age() << endl;print_real_age(t1);return 0;
}
2.友元類
當B是A的友元類時,類B就可以訪問類A的所有成員,
需要注意的是:
? ? ? ? (1)友元類不具備交換性:A聲明B為友元 → B可訪問A的私有成員,但A不能訪問B的私有成員。
????????(2)友元類不具備傳遞性:A是B的友元,B是C的友元 →?A不是C的友元。
????????(3)友元類不具備繼承性:基類友元不會繼承給派生類。
#include <iostream>
#include <string>using namespace std;class Techer{
private:int age = 45;string name = "zhangsna";
public:Techer(int age,string name):age(age),name(name){}//Techer():age(45),name("lisi"){}int get_age()const{return age;}string get_name()const{return name;}void print()const{cout << get_age() << endl;cout << get_name() << endl;}int print_age(){return 18;}friend class student;
};class Student{
public:void get_name(Techer &t){cout << t.get_name() << endl;}
};int main (){Techer t1(34,"zhangsan");Student s1;s1.get_name(t1);return 0;
}
?友元成員函數
#include <iostream>
#include <string>using namespace std;class Techer; //聲明類class Student{
public:void print_name(Techer &t);
};class Techer{
private:int age = 45;string name = "zhangsna";
public:Techer(int age,string name):age(age),name(name){}//Techer():age(45),name("lisi"){}int get_age()const{return age;}string get_name()const{return name;}void print()const{cout << get_age() << endl;cout << get_name() << endl;}int print_age(){return 18;}friend void Student::print_name(Techer &t); //1.友元聲明
};//4.實現友元函數
void Student::print_name(Techer &t){cout << t.get_name() <<endl;
}int main (){Techer t1(34,"zhangsan");Student s1;s1.print_name(t1);return 0;
}
二.函數運算符重載
C++中函數是可以重載的,運算符也是一種特殊的函數,也可以重載,這樣一來,本來只能基本數據類型能做的加減乘除也可以讓對象直接加減乘除。
函數的三要素是:函數名,參數和返回值。
下面就是C++中運算符+含義,所以我們對+函數進行重載,就可以實現對象之間相加。
可以被重載的運算符:(了解)
????????算術運算符: +、-、*、人、%、++、--
????????位操作運算符:&、|、~、^(位異或)、<<(左移)、>>(右移)
????????邏輯運算符:!、&&、
????????比較運算符:<、>、>=、《=、==、!=
????????賦值運算符:=、+=、-=、*=、/=、%=、&=、 I=、^=、《<=、>>=
????????其他運算符:范、0、->、、new、delete、new[、delete[
不被重載的運算符:
????????成員運算符""、指針運算符“*”、三目運算符"?:“、sizeof、作用域""
1.使用友元函數運算符重載
函數名:+
參數:const Integer &i1和const Integer &i2
返回值:Inteder
friend Integer operator +(const Integer &i1,const Integer &i2);
#include <iostream>
#include <string>using namespace std;class Integer{
private:int value;
public:Integer(int value):value(value){}int get_value()const{return value;}void set_value(int a){value = a;}friend Integer operator +(const Integer &i1,const Integer &i2);friend Integer operator ++(Integer &i);friend Integer operator ++(Integer &i,int);
};Integer operator +(const Integer &i1,const Integer &i2){return i1.value + i2.value; //返回兩個int類型,這個時候編譯器會自動幫你創建一個Integer類型的副本,類似于:return Integer(i1.value + i2.value);
}
Integer operator ++(Integer &i){return ++i.value;
}
Integer operator ++(Integer &i,int){return i.value++;
}int main (){Integer i1(3);Integer i2(4);Integer i3 = i1 + i2;cout << i3.get_value() << endl;++i3;cout << (i3++).get_value() << endl;cout << i3.get_value() << endl;return 0;
}
2.使用成員函數運算符重載
簡單三步實現:(投機取巧的方式)
????????(1).把友元函數實現的代碼的friend去掉
????????(2).再把所有傳入的第一個參數去掉,函數內使用this指針替換(不加也可以,編譯器自動識別)
????????(3).在函數名前加上作用域限定符(Integer::)
正常直接寫,就是寫成員函數的步驟。
#include <iostream>
#include <string>using namespace std;class Integer{
private:int value;
public:Integer(int value):value(value){}int get_value()const{return value;}void set_value(int a){value = a;}Integer operator +(const Integer &i);Integer operator ++();Integer operator ++(int);
};Integer Integer::operator +(const Integer &i){return value + i.value;
}
Integer Integer::operator ++(){return ++value;
}
Integer Integer::operator ++(int){return value++;
}int main (){Integer i1(3);Integer i2(4);Integer i3 = i1 + i2;cout << i3.get_value() << endl;++i3;cout << (i3++).get_value() << endl;cout << i3.get_value() << endl;return 0;
}
3.特殊運算符重載
3.1復制運算符重載
如果程序員不手寫賦值運算符重載函數,則編譯器會自動為每個類增加一個賦值運算符重載函數。
在 C++ 中,賦值運算符(operator=
)必須被重載為類的成員函數,而不能作為友元函數或全局函數。這一設計決策源于 C++ 語言的核心特性:
(1)、根本原因:語言規范要求
C++ 標準(ISO/IEC 14882)明確規定:賦值運算符(
=
)必須被聲明為類的非靜態成員函數。這是語言規范層面的硬性規定,違反此規則將導致編譯錯誤。(2)、技術原因:this 指針機制
1. 成員函數的隱式 this 指針
成員函數自動獲得隱式?
this
?指針,指向調用對象賦值操作需要修改左操作數的狀態,
this
?提供直接訪問2. 友元函數缺少 this 指針
友元函數沒有隱式?
this
?指針必須顯式聲明兩個參數(左操作數和右操作數)
違反賦值運算符的二元操作符語法形式
#include <iostream>
#include <string>using namespace std;class String{
private:string str;
public:String(string str):str(str){}string get_str(){return str;}void set_str1(string str){this->str = str;}String& operator =(const String& a){ //系統默認自帶的this->str = a.str;return *this;}
};int main (){String s1("hello ");String s2("world");s1 = s2;cout << s1.get_str() << endl;return 0;
}
3.2類型轉換運算符重載
如果我們想把string類型轉換為成員變量,如果正好你的成員變量只有一個string類型的成員變量,這個時候編譯器會自動幫你賦值優化,String s1 = name;就不會報錯,但是你要是想把s1對象賦值給string類型的name,這個時候編譯器就無法識別,需要使用成員函數進行重載。
可以把注釋的部分去掉,錯誤就沒了,注視部分就是類型轉換重載。
#include <iostream>
#include <string>using namespace std;class String{
private:string str;
public:String(string str):str(str){}string get_str(){return str;}void set_str1(string str){this->str = str;}/*operator string(){return str;}*/
};int main (){String s1("hello ");String s2("world");s1 = s2;string name = s1;cout << name << endl;cout << s1.get_str() << endl;return 0;
}
4.注意:
1.重載的運算符只能對C++語言中已有的運算符進行操作,不能創建新的運算符;
2.運算符重載也是函數重載;
3.運算符不能改變運算符的優先級(*的優先級比+大,不能改變)和結合性(a*b就是a*b不能ab*運算),也不能改變操作數和語法結構;
4.運算符重載函數不支持默認參數,即運算符的操作數數量固定(如二元運算符需要兩個操作數)
5.運算符重載函數的操作數中一定包含自定義類型(運算符重載函數的操作數中必須至少有一個是用戶定義類型)
三.string字符串常用手冊
C++函數手冊:
通過網盤分享的文件:C++函數手冊 (LibraryFunctions).chm
鏈接: https://pan.baidu.com/s/17gE3aR6VQhQQ10tBZNFfHg 提取碼: 3589
#include <iostream>
#include <string.h>using namespace std;int main()
{string s1 = "ABC";// 隱式調用了下面的,參數為const char*string s2("ABC");string s3 = s2; // 拷貝構造函數cout << s3 << endl; // ABC// 參數1:const char*// 參數2:保留前幾個字符string s4("ABCDE",2);cout << s4 << endl; // ABs1 = "ABCDE";// 參數1:string// 參數2:不保留前幾個字符string s5(s1,2);cout << s5 << endl; // CDE// 參數1:字符數量// 參數2:字符內容string s6(3,'Z');cout << s6 << endl; // ZZZswap(s5,s6);cout << s5 << " " << s6 << endl; // ZZZ CDE// 向后追加字符串cout << s1.append("...").append(s5) << endl;// 向后追加單字符s6.push_back('O');cout << s6 << endl; // CDEO// 參數1:插入的位置// 參數2:插入的內容s6.insert(1,"222");cout << s6 << endl;// 參數1:刪除的起始位置// 參數2:刪除的字符數s6.erase(1,3);cout << s6 << endl;// 參數1:替換的起始位置// 參數2:替換的字符數// 參數3:替換的字符s6.replace(0,3,"xxxxxxx");cout << s6 << endl;s6.clear(); // 清空// 判斷是否為空cout << s6.empty() << endl; // 1char c[20];s6 = "ABCDEFG";// 參數1:拷貝的目標// 參數2:拷貝的字符數// 參數3:拷貝的起始點s6.copy(c,2,3);cout << c << endl;char c2[20];s6 = "ABCDEFG";// c_str返回的char*指向一個內部數組,相對不可控cout << s6.c_str() << endl;// 因此需要拷貝出來strcpy(c2,s6.c_str());cout << c2 << endl;return 0;
}
練完后會不會有個疑問,為什么string有這么多操作,之前說過他不是基本數據類型,因為它就是C++中定義好的類。