背景

在工作中,經常用到父類指針指向子類對象,通過virtual關鍵字實現多態;但當突然子類對象想向父類傳數據時,一下子愣住了。重新回顧下C++基礎

一、子類向父類傳遞數據

1. 基本調用方式

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. 通過構造函數傳遞數據

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. 重寫虛函數,回調

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. 通過構造函數傳遞

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. 通過保護成員變量直接訪問

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.通過保護函數傳遞數據

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.通過事件/回調機制

核心點: 利用回調函數,父類在某個事件發生時(如數據接收完成),調用子類的特定方法。在工作項目中,推薦該做法

#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、父類以及子類抽象類

#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、子類具體定義

#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、具體使用

#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;
}

運行結果:


C++ 子類父類的相互轉換_51CTO博客_子類