正好这学期学的 OOP 是 C 艹的,不愧是我,预习了属于是
成员函数补充
析构
复制与拷贝
静态数据成员
静态成员函数
友元
Reference:
成员函数
以下面的例子说明成员函数的两种类型以及三种定义方式
非内联成员函数 - 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Point { public : void setpoint (int , int ) ; int getx () ; int gety () ; private : int x, y; }; void Point::setpoint (int a, int b) { x = a; y = b; } void Point::getx () { return x; } void Point::gety () { return y; }
这个例子中,在类内成员函数声明时,与普通的函数类似,此时是非内联的成员函数。
内联成员函数 - 2.1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Point { public : void setpoint (int , int ) { x = a; y = b; } int getx () { return x; } int gety () { return y; } private : int x, y; };
而这样写,即直接在类内定义函数,则是作为内联函数处理,这些函数将被隐式的定义为内联函数。
内联成员函数 - 2.2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Point { public : inline void setpoint (int , int ) ; inline int getx () ; inline int gety () ; private : int x, y; }; inline void Point::setpoint (int a, int b) { x = a; y = b; } inline void Point::getx () { return x; } inline void Point::gety () { return y; }
这样写和 1
的唯一不同之处在于类内声明成员函数时显式的写上
inline
,此时成员函数作为内联函数处理。
值得注意的是,可以在声明函数原型和定义函数时同时写
inline
,也可以只在其中一处 声明
inline
,效果是相同的。
此外,使用 inline
定义内联函数时,必须将类的声明和内联成员函数的定义都放在同一个文件中(.cpp
or .h),否则编译时无法进行代码替换。
析构函数
析构函数是一种特殊的成员函数。它执行与构造函数相反的操作,通常用于执行一些清理任务,例如释放内存。
以下面的例子来说明。
析构函数 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 #include <cmath> #include <iostream> using namespace std;class Complex { public : Complex (double r = 0.0 , double i = 0.0 ); ~Complex (); double abscomplex () ; private : double real; double imag; }; Complex::Complex (double r, double i) { real = r; imag = i; cout << "Constructor called." << endl; } Complex::~Complex () { cout << "Destructor called." << endl; } double Complex::abscomplex () { double t; t = real * real + imag * imag; return sqrt (t); } int main () { Complex A (1.1 , 2.2 ) ; cout << "The absolute value of the complex number is: " << A.abscomplex () << endl; return 0 ; }
编译并运行这段代码,将得到这样的返回结果
1 2 3 4 iy88@DESKTOP:/path/to$ g++ ./t.cc -o ./t && ./t Constructor called. The absolute value of the complex number is: 2.45967 Destructor called.
类中的 ~Complex
即为析构函数
析构函数与构造函数类似,函数名与类名相同,但它前面必须加一个波浪号(~)
析构函数不返回任何值,在定义析构函数时,是不能说明它的类型的,即使是
void 也不行。
析构函数没有参数,因此它不能被重载,一个类可以有多个构造函数,但是只能有一个析构函数。
销毁对象时,会自动调用析构函数。
每个类必须有一个析构函数,正如构造函数一样,但是当没有显示定义时,会默认生成一个合成析构函数。
对象的赋值和复制
赋值
本质上是对数据成员 进行赋值,需要注意的是:
在使用对象赋值语句时,两个对象的类型必须相同
两个对象的赋值仅仅复制了数据成员,仍是两个实例(instance)而不是引用
对象赋值通过默认赋值运算符函数实现(=)
要注意浅拷贝问题
拷贝构造函数
是一类特殊的构造函数,形参是本类对象的引用。用现有的对象初始化一个新的对象。
以一个例子说明
拷贝构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std;class Point { public : Point (int a, int b) { x = a; y = b; } Point (const Point &p) { x = 2 * p.x; y = 2 * p.y; } void print () { cout << x << " " << y << endl; } private : int x, y; }; int main () { Point p1 (30 , 40 ) ; Point p2 (p1) ; p1. print (); p2. print (); return 0 ; }
执行这段代码将会得到这样的输出
1 2 3 iy88@DESKTOP:/path/to$ g++ ./t.cc -o ./t && ./t 30 40 60 80
与其他构造函数一样,也需要和类名保持一致。且无显式定义会合成一个默认构造函数
除了 Point p2(p1);
这种调用方法,还可以直接赋值
Point p2 = p1;
静态数据成员
用与类内数据共享,而无需引入全局变量。
以下面的代码作为例子
静态数据成员 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 #include <iostream> using namespace std;class Point { public : Point (int a, int b) { ++point_ct; x = a; y = b; } Point (const Point &p) { ++point_ct; x = 2 * p.x; y = 2 * p.y; } void print () { cout << x << " " << y << " " << point_ct << endl; } private : static int point_ct; int x, y; }; int Point::point_ct = 0 ;int main () { Point p1 (30 , 40 ) ; p1. print (); Point p2 (p1) ; p2. print (); return 0 ; }
执行这段代码将会得到这样的输出
1 2 3 iy88@DESKTOP:/path/to$ g++ ./t.cc -o ./t && ./t 30 40 1 60 80 2
声明一个静态数据成员仅需在常规数据成员声明前加上
static
。
需要注意的是,静态数据成员的初始化与常规数据成员大不相同,静态数据成员的初始化需要在类外 ,且在构造对象之前 进行,一般置于
main
之外,且可以在对象定义之前 访问。
静态数据成员属于类,而不是某一个对象,因此使用
类名::成员名
访问。
静态成员函数
与静态数据成员类似,在常规成员函数声明前加上 static
即可。
若在类外定义静态成员函数,无需再加上 static
。
静态成员函数主要用于访问静态数据成员,而不用于访问常规数据成员。
使用静态成员函数的一个原因是,可以用它再建立任何对象之前调用静态数据成员函数,以处理静态数据成员。
静态成员函数与非静态成员函数的重要区别在于:非静态成员函数有
this
指针,可以直接访问对象实例的非静态数据成员,而静态成员函数没有
this
指针,无法直接访问对象实例的非静态数据成员,单可用通过传入的对象的引用或指针来访问静态数据成员。
友元
友元为类外访问类内私有成员(保护成员)提供了途径。
友元函数
友元函数 既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数,统称为友元函数 ,它可以访问该类所有的成员。
以下面的代码为例。
非成员友元函数
友元函数 - 非成员函数 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 #include <iostream> using namespace std;class Point { public : Point (int a, int b) { x = a; y = b; } Point (const Point &p) { x = 2 * p.x; y = 2 * p.y; } friend void print (Point &) ; private : int x, y; }; void print (Point &p) { cout << p.x << " " << p.y << endl; }int main () { Point p1 (30 , 40 ) ; print (p1); Point p2 (p1) ; print (p2); return 0 ; }
友元函数的声明仅需在函数声明前加上 friend
关键字,因为
友元函数不是成员函数
,因此声明可以放在类任意访问类型的部分。
友元函数不是成员函数,无需在调用时前面加上
类名::
,也因此它没有
this
,无法访问对象的成员,要使其访问对象成员,可以传入对象指针或对象引用。
友元函数可以同时属于多个类,以下面这个例子说明。
多个类的友元函数
两个类的友元函数 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 #include <iostream> using namespace std;class A ;class B ;class A { public : A (int v) : v (v) {}; private : int v; friend void print (const A&, const B&) ; }; class B { public : B (int w) : w (w) {}; private : int w; friend void print (const A&, const B&) ; }; void print (const A& a, const B& b) { cout << a.v << " " << b.w << endl; }int main () { A a (1 ) ; B b (2 ) ; print (a, b); 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 #include <iostream> using namespace std;class A ;class B ;class B { public : B (int w) : w (w) {}; void showA (const A& a) ; private : int w; }; class A { public : A (int v) : v (v) {}; friend void B::showA (const A&) ; private : int v; }; void B ::showA (const A& a) { cout << a.v << endl; }int main () { A a (1 ) ; B b (2 ) ; b.showA (a); 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 #include <iostream> using namespace std;class A ;class B ;class B { public : B (int w) : w (w) {}; void showA (const A& a) ; friend void A::showB (const B&) ; private : int w; }; class A { public : A (int v) : v (v) {}; void showB (const B& b) ; friend void B::showA (const A&) ; private : int v; }; void B ::showA (const A& a) { cout << a.v << endl; }void A ::showB (const B& b) { cout << b.w << endl; }int main () { A a (1 ) ; B b (2 ) ; b.showA (a); a.showB (b); return 0 ; }
这个例子中,class A 的 showB 与 class B 的 showA
互为友元函数,友元的声明需要依赖对方的类定义,编译无法通过。此时可以使用一个
helper 类作为其中一个的 友元类 或者直接使
class A 与 class B 互为友元类,缺点是不够细颗粒。
友元类
一个类也可以作为另一个类的友元,称为友元类。
例如使用友元类解决上面的问题。
友元类 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 #include <iostream> using namespace std;class A ;class B ;class B { public : B (int w) : w (w) {}; void showA (const A& a) ; friend class A ; private : int w; }; class A { public : A (int v) : v (v) {}; void showB (const B& b) ; friend class B ; private : int v; }; void B ::showA (const A& a) { cout << a.v << endl; }void A ::showB (const B& b) { cout << b.w << endl; }int main () { A a (1 ) ; B b (2 ) ; b.showA (a); a.showB (b); return 0 ; }