友元 在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问
目的:让一个类中私有成员对类外特定函数或者类访问
关键字:friend
友元的三种实现:全局函数做友元、类做友元、成员函数做友元
全局函数做友元 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Building { friend void goodFriend (Building * building) ; public : string m_SittingRoom; private : string m_BedRoom; public : Building () { this ->m_SittingRoom = "客厅" ; this ->m_BedRoom = "卧室" ; } }; void goodFriend (Building * building) { cout << "好友访问" << building->m_SittingRoom << endl; cout << "好友访问" << building->m_BedRoom<< endl; } int main () { Building building; goodFriend (&building); return 0 ; }
类做友元 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Building { friend class GoodFriend ; public : string m_SittingRoom; private : string m_BedRoom; public : Building () { this ->m_SittingRoom = "客厅" ; this ->m_BedRoom = "卧室" ; } }; class GoodFriend { public : void visit (Building* building) { cout << "好友访问" << building->m_SittingRoom << endl; cout << "好友访问" << building->m_BedRoom<< endl; } }; int main () { Building building; GoodFriend goodFriend; goodFriend.visit (&building); return 0 ; }
成员函数做友元 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Building ;class GoodFriend { public : void visit1 (Building* building) ; void visit2 (Building* building) ; }; class Building { friend void GoodFriend::visit1 (Building* building) ; public : string m_SittingRoom; private : string m_BedRoom; public : Building () { this ->m_SittingRoom = "客厅" ; this ->m_BedRoom = "卧室" ; } }; void GoodFriend::visit1 (Building* building) { cout << "好友visit1访问" << building->m_SittingRoom << endl; cout << "好友visit1访问" << building->m_BedRoom<< endl; } void GoodFriend::visit2 (Building* building) { cout << "好友visit2访问" << building->m_SittingRoom << endl; } int main () { Building building; GoodFriend goodFriend; goodFriend.visit1 (&building); goodFriend.visit2 (&building); return 0 ; }
运算符重载 通过定义类的成员函数或全局函数,重新赋予运算符新的功能,以适应不同的数据类型
加号运算符重载 成员函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Student { public : int chineseScore; int mathScore; public : Student operator +(Student &s) { Student temp; temp.chineseScore = this ->chineseScore + s.chineseScore; temp.mathScore = this ->mathScore + s.mathScore; return temp; } }; int main () { Student xz1; xz1. chineseScore = 40 ; xz1. mathScore = 40 ; Student xz2; xz2. chineseScore = 40 ; xz2. mathScore = 40 ; Student xb = xz1 + xz2; cout << xb.chineseScore << xb.mathScore << endl; return 0 ; }
全局函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Student { public : int chineseScore; int mathScore; }; Student operator +(Student &s1,Student &s2) { Student temp; temp.chineseScore = s1. chineseScore + s2. chineseScore; temp.mathScore = s1. mathScore + s1. mathScore; return temp; } int main () { Student xz1; xz1. chineseScore = 40 ; xz1. mathScore = 40 ; Student xz2; xz2. chineseScore = 40 ; xz2. mathScore = 40 ; Student xb = xz1 + xz2; cout << xb.chineseScore << xb.mathScore << endl; return 0 ; }
左移(输出)运算符重载 作用:可以输出自定义数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Student { friend ostream & operator <<(ostream &cout,Student &s); private : int chineseScore; int mathScore; public : Student (int chineseScore,int mathScore):chineseScore{chineseScore},mathScore{mathScore}{} }; ostream & operator <<(ostream &cout,Student &s) { cout << s.chineseScore << s.mathScore; return cout; } int main () { int a = 10 ; cout << a << endl; Student s{40 ,40 }; cout << s <<endl; return 0 ; }
递增运算符重载 作用:通过重载递增运算符,实现自己的整型数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class MyInt { friend ostream & operator <<(ostream &cout,MyInt myint); private : int num; public : MyInt ():num{0 }{} MyInt & operator ++() { num++; return *this ; } MyInt operator ++(int ) { MyInt temp = *this ; num++; return temp; } }; ostream & operator <<(ostream &cout,MyInt myint) { cout << "myint:" <<myint.num; return cout; } int main () { MyInt myint; cout << myint <<endl; cout << ++myint <<endl; cout << myint++ <<endl; cout << myint <<endl; return 0 ; }
递减运算符重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class MyInt { friend ostream & operator <<(ostream &cout,MyInt myint); private : int num; public : MyInt ():num{0 }{} MyInt & operator --() { num--; return *this ; } MyInt operator --(int ) { MyInt temp = *this ; num--; return temp; } }; ostream & operator <<(ostream &cout,MyInt myint) { cout << "myint:" <<myint.num; return cout; } int main () { MyInt myint; cout << myint <<endl; cout << --myint <<endl; cout << myint-- <<endl; cout << myint <<endl; return 0 ; }
赋值运算符重载 C++编译器默认给一个类提供的函数: (1)默认构造函数(无参,函数体为空) (2)默认析构函数(无参,函数体为空) (3)默认拷贝构造函数,对属性进行值拷贝(初始化) (4)赋值运算符 operator=, 对属性进行值拷贝(赋值)
浅拷贝 简单的赋值拷贝操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class MyClass {public : int data; string name; MyClass () : data (0 ), name ("" ) {} MyClass (const MyClass& other) : data (other.data), name (other.name) {} MyClass& operator =(const MyClass& other) { if (this != &other) { data = other.data; name = other.name; } return *this ; } ~MyClass (){} }; int main () { MyClass myc1; myc1. data = 10 ; myc1. name = "myc1" ; MyClass myc2 = myc1; MyClass myc3; myc3 = myc1; return 0 ; }
调用默认的拷贝构造函数和赋值运算符重载函数,面对成员变量开辟含有堆空间,会导致默认析构函数,多次释放这个堆空间,导致程序异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Person { public : int *age; Person (int age) { this ->age = new int (age); } ~Person () { if (age!=nullptr ) { delete age; age = nullptr ; } } }; int main () { Person p1{18 }; cout << "p1.age = " <<*p1. age <<endl; Person p2{20 }; cout << "p2.age = " <<*p2. age <<endl; p2 = p1; cout << "<------>" << endl; cout << "p1.age = " <<*p1. age <<endl; cout << "p2.age = " <<*p2. age <<endl; return 0 ; }
深拷贝 在堆区重新申请空间,进行拷贝操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 class Person { public : int *age; Person (int age) { this ->age = new int (age); } ~Person () { if (age!=nullptr ) { delete age; age = nullptr ; } } Person (const Person &p) { age = new int (*p.age); } Person & operator =(const Person &p) { if (this != &p) { if (age!=nullptr ) { delete age; age = nullptr ; } age = new int (*p.age); } return *this ; } }; int main () { Person p1{18 }; cout << "p1.age = " <<*p1. age <<endl; Person p2{20 }; cout << "p2.age = " <<*p2. age <<endl; p2 = p1; cout << "<------>" << endl; cout << "p1.age = " <<*p1. age <<endl; cout << "p2.age = " <<*p2. age <<endl; Person p3{p2}; return 0 ; }
关系运算符重载 作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class Person { public : string name; int age; Person ():name{},age{}{} Person (string name,int age):name{name},age{age}{} bool operator ==(const Person &p) { if (this ->name == p.name && this ->age == p.age) { return true ; } else { return false ; } } bool operator !=(const Person &p) { if (this ->name == p.name && this ->age == p.age) { return false ; } else { return true ; } } }; int main () { Person p1{"Ethaniel" ,22 }; Person p2{"Ethaniel" ,22 }; if (p1 == p2) { cout << "p1和p2相同" << endl; } if (p1 != p2) { cout << "p1和p2不相同" << endl; } return 0 ; }
函数调用运算符重载 基本概念 函数调用运算符: () 由于重载后使用的方式非常像函数的调用,因此称为仿函数 仿函数没有固定写法,非常灵活
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class MyClass { public : void operator () (string str) { cout << str << endl; } void printf1 (string str) { cout << str << endl; } }; void printf2 (string str) { cout << str << endl; } int main () { MyClass myc; myc.printf1 ("e" ); myc ("t" ); printf ("h" ); return 0 ; }
类对象初始化的方式 默认初始化 使用默认的构造函数进行初始化
1 2 3 4 5 6 class MyClass {public : int num; std::string str; MyClass (){} };
值初始化 将对象的每个成员初始化为其对应类型的默认值。
1 2 3 4 5 6 7 8 9 10 11 class MyClass {public : int num; std::string str; MyClass ():num (),str (){} }; int main () { MyClass obj{}; return 0 ; }
初始化列表 在构造函数中使用冒号 : 后跟成员变量列表进行初始化。
1 2 3 4 5 6 7 8 9 10 11 class MyClass {public : int num; std::string str; MyClass (int n, const std::string& s):num (n),str (s){} }; int main () { MyClass obj (10 , "Hello" ) ; return 0 ; }
直接初始化 使用圆括号 () 或花括号 {} 提供初始值进行初始化
1 2 3 4 5 6 7 8 9 10 11 12 class MyClass {public : int num; std::string str; MyClass (int n, const std::string& s):num (n),str (s){} }; int main () { MyClass obj1 (10 , "Hello" ) ; MyClass obj2{10 , "Hello" }; return 0 ; }
拷贝初始化 使用等号 = 或圆括号 () 进行初始化,涉及到拷贝构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class MyClass {public : int num; std::string str; MyClass (int n, const std::string& s) : num (n), str (s) {} MyClass (const MyClass& other) : num (other.num), str (other.str) { std::cout << "拷贝构造函数被调用" << std::endl; } }; int main () { MyClass obj = obj2; MyClass obj (obj2) ; return 0 ; }
拷贝构造函数是通过值来拷贝对象的,因此如果成员变量中包含指针等动态分配的资源,需要特别注意拷贝构造函数的实现,以避免浅拷贝带来的问题
列表初始化 使用花括号 {} 进行初始化,提供了更严格的类型匹配和防止窄化转换的特性。
1 2 3 4 5 6 7 8 9 10 11 12 class MyClass {public : int num; std::string str; MyClass (int n, const std::string& s):num (n),str (s){} }; int main () { MyClass obj{10 , "Hello" }; return 0 ; }
类对象的拷贝 C++中类对象的拷贝操作可以通过拷贝构造函数和拷贝赋值运算符来实现
拷贝构造函数:拷贝构造函数用于创建一个新对象,并将已存在的对象的值复制到新对象中。 语法形式为:class_name(const class_name& other); 默认情况下,编译器会为类生成一个默认的拷贝构造函数,按照逐个成员的方式进行拷贝。
拷贝赋值运算符:拷贝赋值运算符用于将一个已存在的对象的值赋给另一个已存在的对象。 语法形式为:class_name& operator=(const class_name& other); 默认情况下,编译器会为类生成一个默认的拷贝赋值运算符,按照逐个成员的方式进行赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Person { private : std::string name; int age; public : Person (const std::string& name, int age) { this ->name = name; this ->age = age; } Person (const Person& other) { this ->name = other.name; this ->age = other.age; } Person& operator =(const Person& other) { if (this != &other) { name = other.name; age = other.age; } return *this ; } std::string getName () const { return name; } int getAge () const { return age; } }; int main () { Person p1 ("A" , 20 ) ; Person p2 = p1; Person p3 ("B" , 25 ) ; p3 = p1; std::cout << p1. getName () << ", " << p1. getAge () << std::endl; std::cout << p2. getName () << ", " << p2. getAge () << std::endl; std::cout << p3. getName () << ", " << p3. getAge () << std::endl; return 0 ; }
注意事项:
(1)深拷贝 vs 浅拷贝:默认情况下,C++会自动生成浅拷贝的拷贝构造函数和拷贝赋值运算符。浅拷贝只是简单地将原对象的成员变量的值复制给新对象,如果成员变量中有指针等动态内存分配的资源,会导致多个对象共享同一块内存,造成悬垂指针或者重复释放的问题。为了避免这个问题,需要手动实现深拷贝,即对动态分配的资源进行独立复制。
(2)参数传递方式:拷贝构造函数和拷贝赋值运算符的参数都是常量引用(const&)。这是为了确保在调用拷贝构造函数和拷贝赋值运算符时不会修改原对象。
(3)自赋值检查:在拷贝赋值运算符的实现中,需要注意对自赋值的检查。即在执行复制操作之前,检查目标对象是否与源对象是同一个对象,如果是,则不进行复制操作,直接返回。
(4)返回类型:拷贝赋值运算符应该返回一个引用,通常是返回*this,以便实现链式连续赋值。
(5)对象成员的拷贝:在拷贝构造函数和拷贝赋值运算符中,需要手动复制每个成员变量的值。对于指针成员变量,需要进行深拷贝。
需要注意的是,拷贝构造函数和拷贝赋值运算符可以根据具体的业务需求进行适当的定义和实现,确保对象的拷贝和赋值操作是正确且安全的。