类
1. struct和class的区别
如果从C语言的视角来看,所谓类就是能够调用自身成员的结构体。而在C++中,关键字struct
虽然仍旧保留,但已非C语言中的结构体,而是表示默认成员共有的class
。
即在C++中,struct C{/*code*/}
和class C{public:/**/}
并无区别,例如下面两组代码所实现的功能是完全一致的。
1
2
3
4
5
6
7
8
|
//默认成员公有 struct Number{ private ; float val; public : float pubVal; Number( float inVal); }; |
1
2
3
4
5
6
7
|
//默认成员为私有 class Number{ float val; //外部无法直接访问 public : float pubVal; Number( float inVal); }; |
所谓私有成员,就是外部函数不可访问的成员
1
2
3
4
5
6
|
void printPublic(Number num){ cout<<num.pubVal<<endl; } void printPrivate(Number num){ cout<<num.val<<endl; //报错,无法访问私有类型 } |
不过从C语言的视角来看,类也的确保留了一些struct
的风格,其初始化方法与指针调用便是明证。
1
2
3
4
5
6
7
8
9
|
int main(){ Number num{3.14}; //相当于引用构造函数 printNumber(num); Number* pNum = # //指向num的指针 //->表示类指针所指向的成员 cout<<pNum->pubVal<<endl; system ( "pause" ); return 0; } |
输出为
1
2
3
4
|
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe 3.14 3.14 |
2. explicit构造
由于C++对泛型具备十分良好的支持,语言本身的强大可能会导致用户在使用过程中不严谨,继而增大维护成本。例如对于如下构造函数
1
2
3
|
Number::Number( float inVal){ val = inVal; } |
那么下面的几个语句都能够输出正确的值
1
2
3
4
5
6
7
8
9
|
int main(){ Number num{3.14}; printNumber(num); num = 1.414; printNumber(num); printNumber(0.618); system ( "pause" ); return 0; } |
结果为
1
2
3
4
5
6
|
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe 3.14 1.414 0.618 请按任意键继续. . . |
可见这三条语句都没有报错
1
2
3
|
Number num{3.14}; num = 1.414; printNumber(0.618); |
第一条是没有问题的,是简单赋值语句;第二条和第三条则是暗中调用构造函数,将浮点类型的变量转换成了Number
类型,这种意义不明的代码自然会引起维护上的困难。explicit
就为解决这种问题而生的。
将构造函数用explicit
进行标记,可以有效禁止这种隐式转换
1
2
3
4
5
6
7
8
9
10
11
12
|
class Number{ float val; public : explicit Number( float inVal); float pubVal; }; int main(){ Number num{3.14}; num = 1.414; //编译不予通过 printNumber(0.618); //编译不予通过 //... } |
3. const和mutable
顾名思义,二者分别是常量与变量,前者要求成员函数不得修改类的成员变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Number{ float val; public : mutable float pubVal; //注意该变量用了mutable explicit Number( float inVal); void printVal() const ; //该方法用了const }; void Number::printVal() const { cout<<val<<endl; /* val = val+1; //这是不被允许的 */ pubVal = val+1; //这是被允许的 } |
即,const
成员只能修改mutable
成员。
4. 自引用
自引用是一种编程技巧,对于更改类状态的函数,如果将类本身作为返回值,那么就可以实现炫酷而优雅的链式操作。
1
2
3
4
5
6
7
8
9
10
|
class Number{ float val; public : explicit Number( float inVal); Number& addOne(); //其返回值是当前对象的地址 }; Number& Number::addOne(){ cout<<val++<<endl; return * this ; } |
其中,*this
指向调用该成员函数的对象,测试一下
1
2
3
4
5
6
|
int main(){ Number num{3.14}; //相当于引用构造函数 num.addOne().addOne().addOne(); system ( "pause" ); return 0; } |
结果为
1
2
3
4
5
6
|
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe 3.14 4.14 5.14 请按任意键继续. . . |
5. static
顾名思义,静态成员之所以被称为静态,在于其存储位置只有一个。对于一个类而言,无论创建了多少实例,类中的静态变量就只被存储在那一个位置。这意味着静态成员要比对象实例具有更长的生命周期,当一个对象被销毁之后,静态成员并没有被销毁,从而再次被调用的时候,也不必另行分配内存。
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 Number{ float val; static Number defaultNum; public : explicit Number( float inVal=0); static void setDefault( float inVal); void printVal() const ; }; void Number::printVal() const { cout<<val<<endl; } //定义默认Num Number Number::defaultNum{3.14}; void Number::setDefault( float val){ defaultNum = Number{val}; }; Number::Number( float inVal){ val = inVal ? inVal : defaultNum.val; } int main(){ Number num{}; //相当于引用构造函数 num.printVal(); system ( "pause" ); return 0; } |
输出为
1
2
3
|
PS E:\Code\cpp> .\a.exe 3.14 请按任意键继续. . . |
复数的实现
复数有实部和虚部,默认值为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
56
57
58
59
60
|
#include<iostream> using namespace std; class Complex{ float real; //实部 float im; //虚部 static Complex defaultNum; public : explicit Complex( float inReal=0, float inIm=0); static void setDefault( float inReal, float inIm); void printVal() const ; Complex& add( float inReal, float inIm); Complex& minus( float inReal, float inIm); Complex& multi( float inReal, float inIm); Complex& div ( float inReal, float inIm); }; //默认值为{0,0} Complex Complex::defaultNum{0,0}; void Complex::setDefault( float inReal, float inIm){ defaultNum = Complex{inReal, inIm}; }; //打印当前值 void Complex::printVal() const { cout<< "real part: " <<real<<endl; cout<< "image part:" <<im<<endl; } //加法 Complex::Complex( float inReal, float inIm){ real = inReal ? inReal : defaultNum.real; im = inIm ? inIm : defaultNum.im; } Complex& Complex::add( float inReal, float inIm){ real += inReal ? inReal : 0; im += inIm ? inIm : 0; return * this ; } Complex& Complex::minus( float inReal, float inIm){ real -= inReal ? inReal : 0; im -= inIm ? inIm : 0; return * this ; } Complex& Complex::multi( float inReal, float inIm){ float temp = real*inReal - im*inIm; im = real*inIm + im*inReal; real = temp; return * this ; } Complex& Complex:: div ( float inReal, float inIm){ float temp = inReal*inReal + inIm*inIm; float tempReal = (real*inReal + im*inIm)/temp; im = (im*inReal-real*inIm)/temp; real = tempReal; return * this ; } int main(){ Complex num{}; //相当于引用构造函数 num.add(1,2).multi(3,4). div (1,2); num.printVal(); system ( "pause" ); return 0; } |
下面的操作便基于这个复数类进行。
6.成员函数重载
上述的加减乘除运算,默认输入值为实部和虚部的组合,但并不能实现两个Complex的运算。C++支持成员函数的重载。
1
2
3
4
5
6
7
8
9
10
11
12
|
class Complex{ /* 上文中所定义的类的结尾 */ Complex operator+(Complex); Complex operator-(Complex); Complex operator*(Complex); Complex operator/(Complex); //实现类似数乘功能 Complex operator*( float ); Complex operator/( float ); } |
这些函数可以通过最简单的方式定义
1
2
3
4
5
|
Complex& Complex::add(Complex num){ real += num.real; im += num.im; return * this ; } |
也可以通过调用已经定义过的成员函数
1
2
3
4
|
Complex& Complex::multi(Complex num){ multi(num.real, num.im); return * this ; } |
7.运算符重载
在C++中,可以很方便地对一些运算符进行重载,其格式为
1
|
Complex operator+(Complex); |
对于两个复数a和b来说,调用重载之后的运算符a+b
等价于a.operator(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
34
35
36
37
38
39
40
41
|
class Complex{ /* 上文中所定义的类的结尾 */ Complex operator+(Complex); Complex operator-(Complex); Complex operator*(Complex); Complex operator/(Complex); } Complex Complex::operator+(Complex num){ float outReal = real+num.real; float outIm = im+num.im; return Complex{outReal, outIm}; } Complex Complex::operator-(Complex num){ return Complex{real-num.real, im-num.im}; } Complex Complex::operator*(Complex num){ return Complex{real*num.real - im*num.im, real*num.im + im*num.real}; } Complex Complex::operator/(Complex num){ float temp = num.real*num.real + num.im*num.im; return Complex{(real*num.real + im*num.im)/temp, (im*num.real-real*num.im)/temp}; } Complex Complex::operator*( float val){ return Complex{real*val,im*val}; } Complex Complex::operator/( float val){ return Complex{real/val,im/val}; } //主函数 int main(){ Complex temp{1,1}; Complex temp1 = temp-temp*temp*2; temp1.printVal(); temp.printVal(); system ( "pause" ); return 0; } |
测试一下结果为
1
2
3
4
5
6
|
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe real part: 1 image part:-3 real part: 1 image part:1 |
可见操作符虽然被重载了,但运算次序得以保留。
8.new
C语言中通过STRUCT* struct = (STRUCT*)malloc(sizeof(STRUCT))的方式来动态地开辟内存,留待日后使用。
在C++中,new
可以胜任这一工作。
例如
1
2
|
int * p = new int ; int * Q = new int (5); |
对于Complex
类,可以通过指针形式进行实现
1
2
3
4
5
6
7
8
|
int main(){ Complex* temp = new Complex(1,1); temp->add(*temp); temp->printVal(); delete (temp); //销毁temp内存 system ( "pause" ); return 0; } |
其中,->
亦承自C语言,用于类指针调用类成员,其结果为
1
2
3
4
5
|
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe real part: 2 image part:2 请按任意键继续. . . |
9.析构函数
一般通过new
来分配内存空间,需要在调用结束之后使用delete
对内存进行释放,delete的执行过程,便会调用析构函数。
在解释析构函数之前,需要回顾一下构造函数,所谓构造函数,即与类名相同的函数,通过构造函数可以创建一个类的对象,并开辟足够的内存。析构函数即销毁函数,将构造函数开辟的内存销毁掉。
析构函数亦与类名相同,而且无参数无return不可重载,是一个不易理解但易于使用的方法。
1
2
3
4
|
public : explicit Complex( float inReal=0, float inIm=0); //此即析构函数, ~Complex(){} |
10.friend
在复数类中,实部和虚部被封装为私有变量,外部函数是无法访问的。此时,如果希望在其他类中创建一个提取复数实部或虚部的变量,则可以考虑友元机制。
所谓友元机制,即允许一个类将其非共有成员授权给指定的函数或者类,通过关键字friend
修饰。例如,
1
2
3
4
5
6
7
8
9
|
/* Complex类 */ friend float getReal(Complex num); }; float getReal(Complex num){ cout<<num.real<<endl; return num.real; } |
这样,getReal
就可以直接访问Complex
类的私有成员。
11.类的继承
一般来说,复数 a + b i a+b\text{i} a+bi并不支持类似直乘的操作,即 ( a + b i ) ∗ ( a + b i ) ≠ a b + c d i (a+b\text{i})*(a+b\text{i})\not ={ab+cd\text{i}} (a+bi)∗(a+bi)=ab+cdi,那么如果希望构造一种新的代数关系,使之既支持复数乘法,又可以直乘,那么就需要新建一个类,为了避免代码过于重复,这个类可以作为复数类的派生类而存在。
需要注意的一点是,此前所创建的Complex
类默认成员为私有,所以其im
,real
对于子类而言是不可访问的。出于简单考虑,我们将class
改为struct
,这样其子类便可无痛调用。
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
|
//Complex类的派生类 class antiComplex : Complex{ public : antiComplex( float inReal, float inIm){ real = inReal; im = inIm; }; void printVal(); antiComplex operator*(antiComplex); }; antiComplex antiComplex::operator*(antiComplex num){ return antiComplex{real*num.real,im*num.im}; } //重写printVal函数 void antiComplex::printVal(){ cout<< "I'm antiComplex" <<endl; cout<< "real part: " <<real<<endl << "image part:" <<im<<endl; } int main(){ antiComplex temp{1,2}; temp.printVal(); temp = temp*temp; temp.printVal(); system ( "pause" ); return 0; } |
其结果为
1
2
3
4
5
6
7
8
|
PS E:\Code\cpp> .\a.exe I'm antiComplex real part: 1 image part:2 I'm antiComplex real part: 1 image part:4 请按任意键继续. . . |
在C++中有三种继承方式,分别是public
,private
,protected
,一般默认为public
继承,其特点是无法访问父类的私有成员;private
继承则连公有成员和保护成员都无法访问;protected
则允许其子类访问,但不允许子类的子类访问。
具体表现如下表所示,其读法为public成员在private继承时表现为private成员。
public继承 | private继承 | protected继承 | |
---|---|---|---|
public成员 | public | private | protected |
private成员 | private | private | private |
protected成员 | protected | private | protected |
12.多态
所谓多态就是多个子类继承一个基类时的差异性,例如,Complex
和antiComplex
都可以作为一种抽象数据结构的子类,毕竟二者只有在乘除法上表现不同。
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
|
#include<iostream> using namespace std; struct Abstract{ float real; float im; Abstract( float inReal, float inIm){ real = inReal; im = inIm; } void printVal(){ cout<< "I'm Abstract" <<endl; }; Abstract& multi(Abstract val){}; }; struct Complex:Abstract{ Complex( float inReal, float inIm) :Abstract(inReal,inIm){} void printVal(); Abstract& multi(Abstract val); }; void Complex::printVal(){ cout<< "I'm Complex:" <<real<< "+" <<im<< "i" <<endl; } Abstract& Complex::multi(Abstract val){ float temp = real*val.real - im*val.im; im = real*val.real + im*val.im; real = temp; return * this ; } struct antiComplex:Abstract{ antiComplex( float inReal, float inIm) :Abstract(inReal,inIm){} void printVal(); Abstract& multi(Abstract val); }; void antiComplex::printVal(){ cout<< "I'm antiComplex:" <<real<< "+" <<im<< "j" <<endl; } Abstract& antiComplex::multi(Abstract val){ real = real*val.real; im = im*val.im; return * this ; } int main(){ Complex temp{1,2}; antiComplex antemp{1,2}; temp.multi(temp).multi(temp); antemp.multi(antemp).multi(temp); temp.printVal(); antemp.printVal(); system ( "pause" ); return 0; } |
其输出结果为
1
2
3
4
5
|
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe I'm Complex:-3+5i I'm antiComplex:1+4j 请按任意键继续. . . |
可见这个结果是错的,原因在于multi
函数的返回指针为Abstract
类型,而基类中的multi
为一个空函数,所以只执行一次multi
。所以,如果子类再调用函数之后继续保持子类的方法就好了,这就需要使用关键字virtual
。
13.virtual
直接通过指针来说明virtual的功能比较合适。
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
|
struct Abstract{ float real; float im; Abstract( float inReal, float inIm){ real = inReal; im = inIm; } void printVal(){ cout<< "I'm Abstract" <<endl; }; }; struct Complex:Abstract{ Complex( float inReal, float inIm) :Abstract(inReal,inIm){} void printVal(){ cout<< "I'm Complex:" <<real<< "+" <<im<< "i" <<endl; } }; int main(){ Abstract* a; Complex temp{1,2}; a = &temp; a->printVal(); system ( "pause" ); return 0; } |
其运行结果为
1
2
3
|
PS E:\Code\cpp> g++ .\oop.cpp PS E:\Code\cpp> .\a.exe I'm Abstract |
也就是说,虽然父类的指针指向了子类的对象,但最终指针指向的仍然是父类的函数。而如果在父类函数前加上virtual
关键字,即将其改为
1
2
3
4
5
6
7
8
9
10
11
|
struct Abstract{ float real; float im; Abstract( float inReal, float inIm){ real = inReal; im = inIm; } virtual void printVal(){ cout<< "I'm Abstract" <<endl; }; }; |
则其输出为
1
2
|
PS E:\Code\cpp> .\a.exe I'm Complex:1+2i |
可见虚函数的作用是使得父类指针指向实际对象的方法。将父类的multi
函数变为
1
|
virtual Abstract& multi(Abstract val){}; |
则最终的输出结果为
1
2
3
|
PS E:\Code\cpp> .\a.exe I'm Complex:-16+34i I'm antiComplex:-16+136j |
看的不过瘾?
以上就是C++面向对象入门全面详解的详细内容,更多关于C++面向对象入门的资料请关注服务器之家其它相关文章!
原文链接:https://blog.csdn.net/m0_37816922/article/details/103769447