C++父类与子类进行交互

背景

在工作中,经常用到父类指针指向子类对象,通过virtual关键字实现多态;但当突然子类对象想向父类传数据时,一下子愣住了。重新回顾下C++基础

一、子类向父类传递数据

1. 基本调用方式

c++ 复制代码
class Parent
{
public:
    void parentFunc()
    {
        std::cout << "parent func\n";
    }

    virtual void virtualFunc()
    {
        std::cout << "virtual func of parent\n";
    }
};

class child :public Parent
{
public:
    void callParentFunc()
    {
        //直接调用父类非虚函数
        parentFunc();

        //调用父类虚函数
        virtualFunc();

        //显示调用父类版本的虚函数
        Parent::virtualFunc();
    }

    void virtualFunc() override
    {
        std::cout<< "child override func of parent\n";

        Parent::virtualFunc();
    }
};

int main()
{
    Parent p;
    child c;

    c.callParentFunc();
    std::cout << "*************************\n";

    c.virtualFunc();

    return 0;
}

***注意:***继承方式都统一为public,如果是其余两个,需要注意下权限问题

2. 通过构造函数传递数据

c++ 复制代码
class Parent
{
public:
    Parent(std::string s, int v) :m_name(s), m_value(v)
    {
        std::cout << "construct function of Parent.\n";
    }
protected:
    std::string m_name;
    int m_value;
};

class child :public Parent
{
public:
    child(std::string s, int v, float data)         //当继承时,尽量使用初始化列表方式
        :Parent(s, v), m_data(data)
    {
        std::cout << "construct function of child.\n";
    }
private:
    float m_data;
};

int main()
{
    child c("test", 2, 9.0f);

    return 0;
}

3. 重写虚函数,回调

c++ 复制代码
class Parent
{
public:
    void func1()
    {
        std::cout << "parent func1\n";

        //调用子类提供的数据
        std::string childData = getDataFromChild();
        std::cout << "child say: " << childData << std::endl;
    }

    virtual std::string getDataFromChild() = 0;
protected:
    std::string m_name;
    int m_value;
};

class child :public Parent
{
public:
    child(float t) :m_data(t)
    {

    }

    std::string getDataFromChild()override
    {
        return "test2  ......";
    }
private:
    float m_data;
};

int main()
{
    child c(9.0f);
    c.func1();

    return 0;
}

二、父类向子类传递数据

核心点: 在于protected权限上

1. 通过构造函数传递

c++ 复制代码
class Parent
{
public:
    Parent(std::string s, int v) :m_name(s), m_value(v)
    {
        std::cout << "construct function of Parent.\n";
    }
protected:
    std::string m_name;
    int m_value;
};

class child :public Parent
{
public:
    child(const std::string& s, const int& value, const float& data)
        :Parent(s, v), m_data(data)
    {
        std::cout << "construct function of child.\n";
    }
private:
    float m_data;
};

int main()
{
    child c("test", 2, 9.0f);
    return 0;
}

注意: 这里父类的构造函数是public的,如果是protectedprivate,则无法通过这种方式调用。

2. 通过保护成员变量直接访问

c++ 复制代码
class Parent {
protected:  // 关键:protected 让子类可以访问
    string sharedData;
    vector<int> dataList;
    
public:
    Parent() : sharedData("默认数据") {}
    
    void setSharedData(const string& data) {
        sharedData = data;
    }
};

class Child : public Parent {
public:
    void useParentData() {
        // 子类直接访问父类的保护成员
        cout << "使用父类数据: " << sharedData << endl;
        
        // 修改父类数据
        sharedData = "子类修改后的数据";
        dataList.push_back(42);
    }
    
    void displayAllData() {
        cout << "当前数据: " << sharedData << endl;
        cout << "数据列表大小: " << dataList.size() << endl;
    }
};

3.通过保护函数传递数据

c++ 复制代码
class Parent {
protected:
    string processedData;
    
    // 保护方法 - 只有子类可以调用
    void processAndStoreData(const string& rawData) {
        processedData = "处理后的_" + rawData;
        cout << "父类处理并存储数据: " << processedData << endl;
    }
    
    string getProcessedData() const {
        return processedData;
    }

public:
    virtual void execute() = 0;  // 纯虚函数
};

class Child : public Parent {
public:
    void execute() override {
        // 子类调用父类的保护方法来处理数据
        processAndStoreData("子类的原始数据");
        
        // 使用父类处理后的数据
        cout << "子类使用处理后的数据: " << getProcessedData() << endl;
    }
};

4.通过事件/回调机制

核心点: 利用回调函数,父类在某个事件发生时(如数据接收完成),调用子类的特定方法。在工作项目中,推荐该做法

c++ 复制代码
#include <functional>
#include <vector>

class EventHandler {
protected:
    vector<function<void(const string&)>> dataCallbacks;
    
public:
    void registerDataCallback(function<void(const string&)> callback) {
        dataCallbacks.push_back(callback);
    }
    
    void notifyDataReceived(const string& data) {
        cout << "父类收到数据,通知子类..." << endl;
        for (auto& callback : dataCallbacks) {
            callback(data);  // 调用子类注册的回调
        }
    }
};

class ChildComponent : public EventHandler {
public:
    ChildComponent() {
        // 注册回调函数,接收父类传递的数据
        registerDataCallback([this](const string& data) {
            this->onParentDataReceived(data);
        });
    }
    
private:
    void onParentDataReceived(const string& data) {
        cout << "子类收到父类数据: " << data << endl;
        // 处理数据...
    }
};

像一些框架有自己独特的方式,比如Qt的信号与槽机制<实质也是回调>。

三、整合

**设计下场景:**一个父类统一做资源管理,进行数据的接收与发送,子类负责特定的业务逻辑,并通过父类来发送数据

3.1、父类以及子类抽象类

c++ 复制代码
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <memory>

class Child;

class Parent
{
protected:
	std::vector<std::string>	m_receivedData;			//接收到的数据
	std::vector<std::string>	m_dataToSend;			//要发送的数据
	std::vector<Child*>			m_childrenObjs;			//所有子类对象

public:
	virtual ~Parent() {}

	void receiveData(const std::string& data);			//统一接收数据
	void sendData(const std::string& data);				//统一发送数据

	void registerChild(Child* c);						//动态注册子类

	void distributeToChildren();						//分发数据给子类

	std::vector<std::string> getPendingData()const { return m_dataToSend; }			//获取要发送的数据
};

class Child
{
protected:
	Parent* m_parentObj;				//父类引用,用于回调
	std::string m_name;

public:
	Child(const std::string& n, Parent* p)
		:m_name(n), m_parentObj(p) {
	}

	virtual ~Child()
	{

	}

	virtual void processData(const std::string& data) = 0;

	void requestSendData(const std::string& data);				//子类向父类请求发数据

	std::string getName()const { return m_name; }
};

//具体实现
#include "Parent.h"

void Parent::receiveData(const std::string& data)
{
	std::cout << "Parent receive data: " << data << std::endl;
	this->m_receivedData.emplace_back(data);

	this->distributeToChildren();
}

void Parent::sendData(const std::string& data)
{
	std::cout << "Parent send data: " << data << std::endl;
	this->m_dataToSend.emplace_back(data);
}

void Parent::registerChild(Child* c)
{
	this->m_childrenObjs.emplace_back(c);
	std::cout << "name of child: " << c->getName() << std::endl;
}

void Parent::distributeToChildren()
{
	for (const auto& data : m_receivedData) {
		for (auto child : m_childrenObjs) {
			child->processData(data);				//各个子类自己处理
		}
	}

	m_receivedData.clear();
}

void Child::requestSendData(const std::string& data)
{
	std::cout << m_name << " request to send data\n";
	m_parentObj->sendData("[" + m_name + "]" + data);
}

3.2、子类具体定义

c++ 复制代码
#pragma once
#include "Parent.h"

class ChildA : public Child
{
public:
	ChildA(Parent* p)
		:Child("childA", p) 
	{
	}

	void processData(const std::string& data)override
	{
		std::cout << m_name << " find " << data << std::endl;
	}
};


class ChildB :public Child
{
public:
	ChildB(Parent* p)
		:Child("childB", p){ }

	void processData(const std::string& data)override
	{
		std::cout << m_name << " find " << data << std::endl;
	}
};

3.3、具体使用

c++ 复制代码
#include <iostream>

#include "ChildA.h"

int main()
{
	//1.创建父类
	Parent p;

	//2.创建各个子类
	ChildA tmp1(&p);
	ChildB tmp2(&p);
	std::cout << "************************************\n";

	//3.子类向父类注册
	p.registerChild(&tmp1);
	p.registerChild(&tmp2);
	std::cout << "************************************\n";
	//4.接收数据
	p.receiveData("hello world.");
	p.receiveData("test data.");
	p.receiveData("cat dog.");
	std::cout << "************************************\n";

	//5.查看发送数据
	auto pendingData = p.getPendingData();
	std::cout << "size is " << pendingData.size() << std::endl;
	for (const auto& data : pendingData) {
		std::cout << "	" << data << std::endl;
	}

	return 0;
}

运行结果:

相关推荐
杯莫停丶11 小时前
设计模式之:命令模式
设计模式·命令模式·1024程序员节·活动勋章
野犬寒鸦11 小时前
从零起步学习MySQL || 第十章:深入了解B+树及B+树的性能优势(结合底层数据结构与数据库设计深度解析)
java·数据库·后端·mysql·1024程序员节
Never_Satisfied11 小时前
在JavaScript / HTML中,无法通过开发者工具查看DOM元素中input里输入的密码
1024程序员节
程序猿阿伟11 小时前
《打破数据孤岛:3D手游角色表情骨骼协同的实践指南》
1024程序员节
kura_tsuki11 小时前
[Docker 集群] 私有仓库 + compose
1024程序员节
时间的情敌11 小时前
前端实现大文件上传全流程详解
1024程序员节
冬天的雪200811 小时前
SpringBoot知识点总结
1024程序员节
JoannaJuanCV11 小时前
大模型基础:Rotary Position Embedding(RoPE)
大模型·1024程序员节·qwen3
库库林_沙琪马11 小时前
SpringBoot内容协商机制
1024程序员节