背景
在工作中,經常用到父類指針指向子類對象,通過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的,如果是protected或private,則無法通過這種方式調用。
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;
}
運行結果: