IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++之5|组合与继承 -> 正文阅读

[C++知识库]C++之5|组合与继承

五、组合与继承

1、回顾标准io流

在C语言中,键盘输入打印信息如下

#include <stdio.h>

int main()
{
    printf("input:");fflush(stdout);
    
    char buf[100];
    fgets(buf);
    
    printf("%s\n",buf);
}

运行结果

@ubuntu:/mnt/hgfs/ub2$ gcc cin_cout1.c
cin_cout1.c: In function ‘main’:
cin_cout1.c:8:5: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
     gets(buf);
     ^~~~
     fgets
/tmp/ccOXH43A.o: In function `main':
cin_cout1.c:(.text+0x44): warning: the `gets' function is dangerous and should not be used.
@ubuntu:/mnt/hgfs/ub2$ ./a.out
input:dskfjsdoj
dskfjsdoj
@ubuntu:/mnt/hgfs/ub2$ 

例22、在C++中,io流表示

#include <stdio.h>
#include <iostream>

using namespace std;

int main()
{
 //   printf("input:");fflush(stdout);
    cout<<"input:";
    char buf[100];
//    gets(buf);
    cin>>buf;
//    printf("%s\n",buf);
    cout<<buf<<endl;
}

运行结果

@ubuntu:/mnt/hgfs/ub2$ g++ cin_cout2.cpp 
@ubuntu:/mnt/hgfs/ub2$ ./a.out
input:soadjaofaoh
soadjaofaoh
@ubuntu:/mnt/hgfs/ub2$ 

当然,除此之外还可以直接打印数字,以及其对应的进制数

#include <stdio.h>
#include <iostream>

using namespace std;

int main()
{
 //   printf("input:");fflush(stdout);
    cout<<"input:";
    char buf[100];
//    gets(buf);
    cin>>buf;
//    printf("%s\n",buf);
    cout<<buf<<endl;

    cout<<10<<endl;
    cout<<hex<<10<<endl;
}

运行结果

@ubuntu:/mnt/hgfs/ub2$ g++ cin_cout3.cpp 
@ubuntu:/mnt/hgfs/ub2$ ./a.out
input:dsofhsighshgo
dsofhsighshgo
10
a
@ubuntu:/mnt/hgfs/ub2$ 

2、组合

假如现在需要做个学生管理系统类的应用,然后想起之前做过一个有尾部追加功能的类,在这里有机会用上,于是,组合就派上了用场。

比如说,之前做的尾部追加功能数组类叫ARR,现在想用在目前的学生管理系统类stuma里,实现如下

例23、组合

arr.cpp

#include "arr.h"
#include <stdio.h>

//ARR类的两个成员函数,addtail和show
void ARR::addtail(int data)
{
	this->data[tail++] = data;
}

void ARR::show(void)
{
	int i = 0;
	for(;i<tail; i++)
		printf("%d, ", data[i]);
	printf("\n");
}

arr.h

#ifndef _ARR_
#define _ARR_

class ARR{
public:
	ARR():tail(0){
	}

	void addtail(int data);
	void show(void);
private:
	int data[100];
	int tail;
};


#endif

main.cpp

#include "arr.h"

class Stuma{
public:
	Stuma(){

	}
	~Stuma() { }
	
	void savescore(int score)
	{
		scorearr.addtail(score);
	}

	void showscore(void)
	{
		scorearr.show();
	}

private:
	ARR scorearr;

};

int main()
{
	Stuma mmm;

	mmm.savescore(23);
	mmm.savescore(44);
	mmm.savescore(55);
	mmm.savescore(23);

	mmm.showscore();
}

运行结果

@ubuntu:/mnt/hgfs/ub2/ARR1$ g++ *.cpp
@ubuntu:/mnt/hgfs/ub2/ARR1$ ./a.out
23, 44, 55, 23, 
@ubuntu:/mnt/hgfs/ub2/ARR1$ 

上例的addtail()本来是属于ARR类的,让它组合其它功能实现一起(本例没组合其它功能实现)进行再次封装,得到属于stuma类的savescore(),同理,ARR类的show()经过再次封装得到stuma类的showscore()。示意图如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u7dzRkSj-1655908744452)(D:\Typora_pic\1655434361516.png)]

组合的妙处在于化他人之功能为己用,很多时候我们会接触这种情况:开发东西需要用到A、B、C、D等功能,然后你手头有关于A、B、C、D功能实现的大佬的资源(网上开源、公司提供等等),于是就可以使用组合把这些大佬的功能整合为己用。否则,你就得自己去实现ABCD的功能,这样不但耗时耗精力,而且很有可能出现各种各样bug,质量得不到保证。(毕竟大佬的代码是经过千锤百炼得出的精华)

3、继承

例24、继承(1)

首先,简单实现打印x到屏幕

#include <iostream>

using namespace std;

class A{
public:
	A(){ }
	~A(){ }
	void showx()
	{
		cout<<"xxxxxxxxxxxxxxx"<<endl;
	}
};


int main()
{
	A a;
	a.showx();
}

执行结果

@ubuntu:/mnt/hgfs/ub2$ g++ base1_1.cpp 
@ubuntu:/mnt/hgfs/ub2$ ./a.out
xxxxxxxxxxxxxxx
@ubuntu:/mnt/hgfs/ub2$ 

现在我想打印y而不是x,即a.showy(),很明显是不行的,于是就需要对show功能进行升级。

有两种情况:

  1. 能拿到源代码

这种情况就简单了,直接在对应类(class A)里添加对应功能

void showy()
	{
		cout<<"yyyyyyyyyyyyyyy"<<endl;
	}
  1. 拿不到源代码

但很多时候拿不到别人源码,又想用人家的类似功能,于是就需要继承(派生)。

表示形式如下

class AX:public A{
    
};

AXA的派生类,虽然拿不到别人的类A,但可以对类A派生出自己的类AX,然后类AX就继承了类A的功能。接下来只需要对类AX进行自己的编程操作添加需要的功能,便可以不仅实现自己功能而且还拥有所继承的类A的功能。

案例如下

#include <iostream>

using namespace std;

class A{
public:
	A(){ }
	~A(){ }
	void showx()
	{
		cout<<"xxxxxxxxxxxxxxx"<<endl;
	}
};

class AX:public A{
public:
	void showy()
	{
		cout<<"yyyyyyyyyyyyyyy"<<endl;
	}
};

int main()
{
	AX b;
	b.showx();
	b.showy();
}

执行结果

@ubuntu:/mnt/hgfs/ub2$ g++ base1_2.cpp 
@ubuntu:/mnt/hgfs/ub2$ ./a.out
xxxxxxxxxxxxxxx
yyyyyyyyyyyyyyy
@ubuntu:/mnt/hgfs/ub2$ 

可以发现,类AX不仅有了自己的打印y功能,而且继承了类A的打印x功能(也可以说成是类A派生打印x的功能给了AX)。

组合和继承的区分:

组合是把优秀的功能实现进行整合作为自己的功能;而继承(派生)是在优秀的功能实现基础上添加自己的功能,起站在巨人的肩膀上的效率。

举个例子:假如我需求是一根黑线,那么组合就是把各黑点紧密排成一队形成黑线;假如我需要一根红线,那么继承就是以别人的线为基础,然后染成红色。

例25、继承(2)

现在再举个复杂点的例子:

回顾例23,如下

arr.cpp

#include "arr.h"
#include <stdio.h>

//ARR类的两个成员函数,addtail和show
void ARR::addtail(int data)
{
	this->data[tail++] = data;
}

void ARR::show(void)
{
	int i = 0;
	for(;i<tail; i++)
		printf("%d, ", data[i]);
	printf("\n");
}

arr.h

#ifndef _ARR_
#define _ARR_

class ARR{
public:
	ARR():tail(0){
	}

	void addtail(int data);
	void show(void);
private:
	int data[100];
	int tail;
};


#endif

main.cpp

#include "arr.h"

class Stuma{
public:
	Stuma(){

	}
	~Stuma() { }
	
	void savescore(int score)
	{
		scorearr.addtail(score);
	}

	void showscore(void)
	{
		scorearr.show();
	}

private:
	ARR scorearr;

};

int main()
{
	Stuma mmm;

	mmm.savescore(23);
	mmm.savescore(44);
	mmm.savescore(55);
	mmm.savescore(23);

	mmm.showscore();
}

这是模拟学生管理系统的功能。现在说要在原基础上增加求平均的功能,如果是一般情况,就去arr.cpparr.h添加功能,类似addtail()和show()那样。如下

arr.cpp

#include "arr.h"
#include <stdio.h>


void ARR::addtail(int data)
{
	this->data[tail++] = data;
}

void ARR::show(void)
{
	int i = 0;
	for(;i<tail; i++)
		printf("%d, ", data[i]);
	printf("\n");
}

int ARR::ever(void)
{
	int i = 0;
	int sum = 0;
	for(;i<tail; i++)
		sum += data[i]; 
	return sum/tail;
}

arr.h

#ifndef _ARR_
#define _ARR_

class ARR{
public:
	ARR():tail(0){
	}

	void addtail(int data);
	void show(void);
	int ever(void);
private:
	int data[100];
	int tail;
};


#endif

main.cpp

#include "arr.h"
#include <iostream>

using namespace std;
class Stuma{
public:
	Stuma(){

	}
	~Stuma() { }
	
	void savescore(int score)
	{
		scorearr.addtail(score);
	}
	
	int everscore(void)
	{
		return scorearr.ever();
	}

	void showscore(void)
	{
		scorearr.show();
	}

private:
	ARR scorearr;
};

int main()
{
	Stuma mmm;

	mmm.savescore(23);
	mmm.savescore(44);
	mmm.savescore(55);
	mmm.savescore(23);

	mmm.showscore();
	cout<<mmm.everscore()<<endl;
}

执行结果:

@ubuntu:/mnt/hgfs/ub2/ARR2_1$ g++ *.cpp 
@ubuntu:/mnt/hgfs/ub2/ARR2_1$ ./a.out
23, 44, 55, 23, 
36
@ubuntu:/mnt/hgfs/ub2/ARR2_1$ 

然而,很多时候你拿不到别人的arr文件,这样的话就需要用继承来实现。先以ARR为基础派生出自己的类ARRX,然后在ARRX里添加功能,接着再把ARRX组合到类stuma。如下

arr.cpp

#include "arr.h"
#include <stdio.h>

void ARR::addtail(int data)
{
	this->data[tail++] = data;
}

void ARR::show(void)
{
	int i = 0;
	for(;i<tail; i++)
		printf("%d, ", data[i]);
	printf("\n");
}

arr.h

#ifndef _ARR_
#define _ARR_

class ARR{
public:
	ARR():tail(0){
	}

	void addtail(int data);
	void show(void);
private:
	int data[100];
	int tail;
};

#endif

main.cpp

#include "arr.h"
#include <iostream>

using namespace std;

class ARRX:public ARR{
public:
	int ever(void)
	{
		int i = 0;
		int sum = 0;
		for(;i<tail; i++)
			sum += data[i]; 
		return sum/tail;
	}
};

class Stuma{
public:
	Stuma(){

	}
	~Stuma() { }
	
	void savescore(int score)
	{
		scorearr.addtail(score);
	}
	
	int everscore(void)
	{
		return scorearr.ever();
	}

	void showscore(void)
	{
		scorearr.show();
	}

private:
	ARR scorearr;
};

int main()
{
	Stuma mmm;

	mmm.savescore(23);
	mmm.savescore(44);
	mmm.savescore(55);
	mmm.savescore(23);

	mmm.showscore();
	cout << mmm.everscore() <<endl;
}

执行结果报错

wu@ubuntu:/mnt/hgfs/ub2/level14/2day/ARR2_2$ g++ *.cpp 
main.cpp: In member function ‘int ARRX::ever()’:
main.cpp:12:10: error: ‘int ARR::tail’ is private within this context
   for(;i<tail; i++)
          ^~~~
In file included from main.cpp:1:0:
arr.h:13:6: note: declared private here
  int tail;
      ^~~~
main.cpp:13:11: error: ‘int ARR::data [100]’ is private within this context
    sum += data[i];
           ^~~~
In file included from main.cpp:1:0:
arr.h:12:14: note: declared private here
  int data[100];
              ^
main.cpp:14:14: error: ‘int ARR::tail’ is private within this context
   return sum/tail;
              ^~~~
In file included from main.cpp:1:0:
arr.h:13:6: note: declared private here
  int tail;
      ^~~~
main.cpp: In member function ‘int Stuma::everscore()’:
main.cpp:32:19: error: ‘class ARR’ has no member named ‘ever’
   return scorearr.ever();
                   ^~~~
wu@ubuntu:/mnt/hgfs/ub2/level14/2day/ARR2_2$ 

这是因为public继承方式中,基类的所有private成员在派生类中不可访问

class ARRX:public ARR//ARRX是派生类,ARR是基类,public表示继承方式

关于继承方式,下图所示

在这里插入图片描述

所以出错原因在于私有成员(private)被限制,除此之外还有个错误是:类ARR派生成ARRX后,组合的应该是ARRX而不是ARR,见错误提示

main.cpp: In member function ‘int Stuma::everscore()’:
main.cpp:32:19: error: ‘class ARR’ has no member named ‘ever’
   return scorearr.ever();

所以把arr.h的私有成员公开出来,然后main.cpp的类stuma私有成员改为ARRX的,如下

arr.cpp

#include "arr.h"
#include <stdio.h>

void ARR::addtail(int data)
{
	this->data[tail++] = data;
}

void ARR::show(void)
{
	int i = 0;
	for(;i<tail; i++)
		printf("%d, ", data[i]);
	printf("\n");
}

arr.h

#ifndef _ARR_
#define _ARR_

class ARR{
public:
	ARR():tail(0){
	}

	void addtail(int data);
	void show(void);
//private:
	int data[100];
	int tail;
};

#endif

main.cpp

#include "arr.h"
#include <iostream>

using namespace std;

class ARRX:public ARR{
public:
	int ever(void)
	{
		int i = 0;
		int sum = 0;
		for(;i<tail; i++)
			sum += data[i]; 
		return sum/tail;
	}
};

class Stuma{
public:
	Stuma(){

	}
	~Stuma() { }
	
	void savescore(int score)
	{
		scorearr.addtail(score);
	}
	
	int everscore(void)
	{
		return scorearr.ever();
	}

	void showscore(void)
	{
		scorearr.show();
	}

private:
	ARRX scorearr;
};

int main()
{
	Stuma mmm;

	mmm.savescore(23);
	mmm.savescore(44);
	mmm.savescore(55);
	mmm.savescore(23);

	mmm.showscore();
	cout << mmm.everscore() <<endl;
}

执行结果:

@ubuntu:/mnt/hgfs/ub2/ARR2_2$ g++ *.cpp 
@ubuntu:/mnt/hgfs/ub2/ARR2_2$ ./a.out
23, 44, 55, 23, 
36
@ubuntu:/mnt/hgfs/ub2/ARR2_2$ 
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-06-29 18:48:02  更:2022-06-29 18:51:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/20 6:58:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码