news 2026/4/30 20:54:24

C++编程面向对象入门全面详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++编程面向对象入门全面详解

1. struct和class的区别

如果从C语言的视角来看,所谓类就是能够调用自身成员的结构体。而在C++中,关键字struct虽然仍旧保留,但已非C语言中的结构体,而是表示默认成员共有的class

即在C++中,struct C{/*code*/}class C{public:/**/}并无区别,例如下面两组代码所实现的功能是完全一致的。

1

2

3

4

5

6

7

8

//默认成员公有

structNumber{

private;

floatval;

public:

floatpubVal;

Number(floatinVal);

};

1

2

3

4

5

6

7

//默认成员为私有

classNumber{

floatval;//外部无法直接访问

public:

floatpubVal;

Number(floatinVal);

};

所谓私有成员,就是外部函数不可访问的成员

1

2

3

4

5

6

voidprintPublic(Number num){

cout<<num.pubVal<<endl;

}

voidprintPrivate(Number num){

cout<<num.val<<endl;//报错,无法访问私有类型

}

不过从C语言的视角来看,类也的确保留了一些struct的风格,其初始化方法与指针调用便是明证。

1

2

3

4

5

6

7

8

9

intmain(){

Number num{3.14};//相当于引用构造函数

printNumber(num);

Number* pNum = &num;//指向num的指针

//->表示类指针所指向的成员

cout<<pNum->pubVal<<endl;

system("pause");

return0;

}

输出为

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(floatinVal){

val = inVal;

}

那么下面的几个语句都能够输出正确的值

1

2

3

4

5

6

7

8

9

intmain(){

Number num{3.14};

printNumber(num);

num = 1.414;

printNumber(num);

printNumber(0.618);

system("pause");

return0;

}

结果为

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

classNumber{

floatval;

public:

explicitNumber(floatinVal);

floatpubVal;

};

intmain(){

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

classNumber{

floatval;

public:

mutablefloatpubVal;//注意该变量用了mutable

explicitNumber(floatinVal);

voidprintVal()const;//该方法用了const

};

voidNumber::printVal()const{

cout<<val<<endl;

/*

val = val+1; //这是不被允许的

*/

pubVal = val+1;//这是被允许的

}

即,const成员只能修改mutable成员。

4. 自引用

自引用是一种编程技巧,对于更改类状态的函数,如果将类本身作为返回值,那么就可以实现炫酷而优雅的链式操作。

1

2

3

4

5

6

7

8

9

10

classNumber{

floatval;

public:

explicitNumber(floatinVal);

Number& addOne();//其返回值是当前对象的地址

};

Number& Number::addOne(){

cout<<val++<<endl;

return*this;

}

其中,*this指向调用该成员函数的对象,测试一下

1

2

3

4

5

6

intmain(){

Number num{3.14};//相当于引用构造函数

num.addOne().addOne().addOne();

system("pause");

return0;

}

结果为

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

classNumber{

floatval;

staticNumber defaultNum;

public:

explicitNumber(floatinVal=0);

staticvoidsetDefault(floatinVal);

voidprintVal()const;

};

voidNumber::printVal()const{

cout<<val<<endl;

}

//定义默认Num

Number Number::defaultNum{3.14};

voidNumber::setDefault(floatval){

defaultNum = Number{val};

};

Number::Number(floatinVal){

val = inVal ? inVal : defaultNum.val;

}

intmain(){

Number num{};//相当于引用构造函数

num.printVal();

system("pause");

return0;

}

输出为

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>

usingnamespacestd;

classComplex{

floatreal;//实部

floatim;//虚部

staticComplex defaultNum;

public:

explicitComplex(floatinReal=0,floatinIm=0);

staticvoidsetDefault(floatinReal,floatinIm);

voidprintVal()const;

Complex& add(floatinReal,floatinIm);

Complex& minus(floatinReal,floatinIm);

Complex& multi(floatinReal,floatinIm);

Complex&div(floatinReal,floatinIm);

};

//默认值为{0,0}

Complex Complex::defaultNum{0,0};

voidComplex::setDefault(floatinReal,floatinIm){

defaultNum = Complex{inReal, inIm};

};

//打印当前值

voidComplex::printVal()const{

cout<<"real part: "<<real<<endl;

cout<<"image part:"<<im<<endl;

}

//加法

Complex::Complex(floatinReal,floatinIm){

real = inReal ? inReal : defaultNum.real;

im = inIm ? inIm : defaultNum.im;

}

Complex& Complex::add(floatinReal,floatinIm){

real += inReal ? inReal : 0;

im += inIm ? inIm : 0;

return*this;

}

Complex& Complex::minus(floatinReal,floatinIm){

real -= inReal ? inReal : 0;

im -= inIm ? inIm : 0;

return*this;

}

Complex& Complex::multi(floatinReal,floatinIm){

floattemp = real*inReal - im*inIm;

im = real*inIm + im*inReal;

real = temp;

return*this;

}

Complex& Complex::div(floatinReal,floatinIm){

floattemp = inReal*inReal + inIm*inIm;

floattempReal = (real*inReal + im*inIm)/temp;

im = (im*inReal-real*inIm)/temp;

real = tempReal;

return*this;

}

intmain(){

Complex num{};//相当于引用构造函数

num.add(1,2).multi(3,4).div(1,2);

num.printVal();

system("pause");

return0;

}

下面的操作便基于这个复数类进行。

6.成员函数重载

上述的加减乘除运算,默认输入值为实部和虚部的组合,但并不能实现两个Complex的运算。C++支持成员函数的重载。

1

2

3

4

5

6

7

8

9

10

11

12

classComplex{

/*

上文中所定义的类的结尾

*/

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

classComplex{

/*

上文中所定义的类的结尾

*/

Complex operator+(Complex);

Complex operator-(Complex);

Complex operator*(Complex);

Complex operator/(Complex);

}

Complex Complex::operator+(Complex num){

floatoutReal = real+num.real;

floatoutIm = im+num.im;

returnComplex{outReal, outIm};

}

Complex Complex::operator-(Complex num){

returnComplex{real-num.real, im-num.im};

}

Complex Complex::operator*(Complex num){

returnComplex{real*num.real - im*num.im,

real*num.im + im*num.real};

}

Complex Complex::operator/(Complex num){

floattemp = num.real*num.real + num.im*num.im;

returnComplex{(real*num.real + im*num.im)/temp,

(im*num.real-real*num.im)/temp};

}

Complex Complex::operator*(floatval){

returnComplex{real*val,im*val};

}

Complex Complex::operator/(floatval){

returnComplex{real/val,im/val};

}

//主函数

intmain(){

Complex temp{1,1};

Complex temp1 = temp-temp*temp*2;

temp1.printVal();

temp.printVal();

system("pause");

return0;

}

测试一下结果为

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 =newint;

int* Q =newint(5);

对于Complex类,可以通过指针形式进行实现

1

2

3

4

5

6

7

8

intmain(){

Complex* temp =newComplex(1,1);

temp->add(*temp);

temp->printVal();

delete(temp);//销毁temp内存

system("pause");

return0;

}

其中,->亦承自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:

explicitComplex(floatinReal=0,floatinIm=0);

//此即析构函数,

~Complex(){}

10.friend

在复数类中,实部和虚部被封装为私有变量,外部函数是无法访问的。此时,如果希望在其他类中创建一个提取复数实部或虚部的变量,则可以考虑友元机制。

所谓友元机制,即允许一个类将其非共有成员授权给指定的函数或者类,通过关键字friend修饰。例如,

1

2

3

4

5

6

7

8

9

/*

Complex类

*/

friendfloatgetReal(Complex num);

};

floatgetReal(Complex num){

cout<<num.real<<endl;

returnnum.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,那么如果希望构造一种新的代数关系,使之既支持复数乘法,又可以直乘,那么就需要新建一个类,为了避免代码过于重复,这个类可以作为复数类的派生类而存在。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 20:52:44

如何在Node.js服务中接入Taotoken并实现异步聊天补全

如何在Node.js服务中接入Taotoken并实现异步聊天补全 1. 环境准备与依赖安装 在开始编写Node.js服务之前&#xff0c;需要确保开发环境已安装Node.js 16或更高版本。推荐使用npm或yarn作为包管理工具。首先创建一个新的项目目录并初始化&#xff1a; mkdir taotoken-node-de…

作者头像 李华
网站建设 2026/4/30 20:52:40

Neo4j-MCP:基于模型上下文协议的图数据库智能接口实践

1. 项目概述&#xff1a;当Neo4j遇见MCP&#xff0c;图数据库的智能接口革命 如果你和我一样&#xff0c;长期在数据工程和AI应用开发的一线摸爬滚打&#xff0c;那你一定对Neo4j这个名字不陌生。作为图数据库领域的标杆&#xff0c;它以其直观的图模型和强大的Cypher查询语言…

作者头像 李华
网站建设 2026/4/30 20:49:24

在Taotoken平台管理多个API Key并设置访问限制的教程

在Taotoken平台管理多个API Key并设置访问限制的教程 1. 创建API Key的基础步骤 登录Taotoken控制台后&#xff0c;导航至「API密钥管理」页面。点击「新建API Key」按钮&#xff0c;系统会生成一个以sk-开头的密钥字符串。创建时建议填写描述字段&#xff0c;例如标注该密钥…

作者头像 李华