鱼C论坛

 找回密码
 立即注册
查看: 2725|回复: 32

[已解决]使用接口(基类)访问模板类中的数据怎么写才漂亮?

[复制链接]
发表于 2020-1-6 20:56:21 | 显示全部楼层 |阅读模式
100鱼币
本帖最后由 Croper 于 2020-1-6 21:31 编辑

本来以为很简单一个问题。。突然发现我纠结到了。

比如说这里有一个模板类A:
  1. template <typename T>
  2. class A{
  3. public:
  4.         //....其他数据成员

  5.         T data;  //某个数据成员

  6.         //..其他成员函数
  7. };
复制代码


为了方便访问它,写了一个接口类,并让模板类继承:
  1. //接口类
  2. struct IA {

  3.         //virtual IA* copy() =0;
  4.         //virtual int id() =0;
  5.         //...其他接口..blalblabla..
  6. };

  7. //模板类
  8. template <typename T>
  9. class A :public IA {
  10. public:
  11.         //....其他数据成员

  12.         T data;  //某个数据成员

  13.         //..其他成员函数
  14. };
复制代码


这个时候,我想通过IA来访问类A中的数据成员data。
类A是模板类,那么这个接口只能写成模板函数。。。
但是模板函数是不能写成虚函数的,想来想去,只想出两种方法:

  1. //接口类
  2. struct IA {
  3. private:
  4.         virtual void* _GetData() = 0;  //返回类型改为void*,这样能写成虚函数
  5. public:
  6.         template <typename T>          //然后再包装一下
  7.         T GetData() {
  8.                 return *static_cast<T*>(_GetData());
  9.         }
  10. };

  11. //模板类
  12. template <typename T>
  13. class A :public IA {
  14. public:
  15.         T data;  //需要的数据成员
  16.         void* _GetData() override {     //这是继承的函数
  17.                 return &data;
  18.         }
  19. };
复制代码

这样写的问题是,即使模板函数GetData的类型和模板类不一致且无法转化,也不会报错,
而且类型之间的互相转化也是直接二进制数据强制转化,这肯定不是所期望的

  1. //接口类
  2. struct IA {
  3. public:
  4.         template <typename T>
  5.         T GetData();
  6. };

  7. //模板类
  8. template <typename T>
  9. class A :public IA {
  10. public:
  11.         T data;  //需要的数据成员
  12. };

  13. template<typename T>
  14. T IA::GetData()
  15. {
  16.         return dynamic_cast<A<T>*>(this)->data;  //将this指针转化为A<T>的指针
  17. };
复制代码

这样首先是不符合设计模式了,抽象类(底层)依赖具体实现(高层)。
另外,同样无法做到double与int的相互转化。

所以还有什么好点的写法么。。?


最佳答案
2020-1-6 20:56:22
我仔细想了想,这个问题应该无解,至少我无解
这个问题要求C++能够记住类型信息,也就是要求C++能够正确读取 void *
试想一下,在new一个对象的时候保存这个对象的类型,然后把new出来的对象给void 指针保存,在使用的时候用之前保存的类型信息对void指针解引用
但是C++好像不能读取void *,我百度不到如何保存类型信息

最佳答案

查看完整内容

我仔细想了想,这个问题应该无解,至少我无解 这个问题要求C++能够记住类型信息,也就是要求C++能够正确读取 void * 试想一下,在new一个对象的时候保存这个对象的类型,然后把new出来的对象给void 指针保存,在使用的时候用之前保存的类型信息对void指针解引用 但是C++好像不能读取void *,我百度不到如何保存类型信息
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-6 20:56:22 | 显示全部楼层    本楼为最佳答案   
我仔细想了想,这个问题应该无解,至少我无解
这个问题要求C++能够记住类型信息,也就是要求C++能够正确读取 void *
试想一下,在new一个对象的时候保存这个对象的类型,然后把new出来的对象给void 指针保存,在使用的时候用之前保存的类型信息对void指针解引用
但是C++好像不能读取void *,我百度不到如何保存类型信息
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-6 21:06:48 | 显示全部楼层
看不懂你想表达什么意思
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-6 21:58:28 | 显示全部楼层
就是我现在想通过接口类访问模板类中的数据data,
但是找不到一个好的实现方法能满足要求:
1、接受的变量和模板类的类型不匹配且无法相互转化时能报错(就像你给一个string赋一个vector时会报错一样)
2、最好接受的变量和模板类的能互相转化的时候时能正常转化(比如一个double类型的变量能够接受一个整数并隐式转化)
3、符合一般来说面向对象的设计原则。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 00:34:32 | 显示全部楼层
"类A是模板类,那么这个接口只能写成模板函数。。。
但是模板函数是不能写成虚函数的"

我不明白你说的这句话是什么意思
通过IA来访问类A中的数据成员data
可以在IA中加一个纯虚函数呀,不过IA也要用模板
写成下面这样,是你想要的吗?

  1. #include <iostream>

  2. //接口类
  3. template <typename T>
  4. struct IA {
  5. public:
  6.         virtual IA* copy() =0;
  7.         virtual uint64_t id() =0;
  8.         //...其他接口..blalblabla..

  9.         virtual T GetData() = 0;
  10. };

  11. //模板类
  12. template <typename T>
  13. class A:public IA<T> {
  14. public:
  15.         IA<T>* copy() {
  16.                 return new A<T>(*this);
  17.         }
  18.         uint64_t id() {
  19.                 return uint64_t(this);
  20.         }
  21.         T GetData() {
  22.                 return this->data;
  23.         }
  24.         //...其他接口..blalblabla..

  25.         //....其他数据成员

  26.         T data;  //某个数据成员

  27.         //..其他成员函数
  28. };

  29. int main()
  30. {
  31.         A<double> a;

  32.         return 0;
  33. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 00:50:54 | 显示全部楼层
  1. #include <iostream>
  2. #include <string>

  3. //接口类
  4. template <typename T>
  5. struct IA {
  6. public:
  7.         virtual IA* copy() =0;
  8.         virtual uint64_t id() =0;
  9.         //...其他接口..blalblabla..

  10.         virtual T GetData() = 0;
  11. };

  12. //模板类
  13. template <typename T>
  14. class A:public IA<T> {
  15. public:
  16.         A(const T &n): data(n) {
  17.         }

  18.         IA<T>* copy() {
  19.                 return new A<T>(*this);
  20.         }
  21.         uint64_t id() {
  22.                 return uint64_t(this);
  23.         }
  24.         T GetData() {
  25.                 return this->data;
  26.         }
  27.         //...其他接口..blalblabla..

  28.         //....其他数据成员

  29.         T data;  //某个数据成员

  30.         //..其他成员函数
  31. };

  32. int main()
  33. {
  34.         IA<double> *a = new A<double>(123.45);
  35.         IA<int> *b = new A<int>(123456);
  36.         IA<std::string> *c = new A<std::string>("abcd");
  37.         std::cout << "id: " << a->id() << std::endl;
  38.         std::cout << "data: " << a->GetData() << std::endl;
  39.         std::cout << "id: " << b->id() << std::endl;
  40.         std::cout << "data: " << b->GetData() << std::endl;
  41.         std::cout << "id: " << c->id() << std::endl;
  42.         std::cout << "data: " << c->GetData() << std::endl;

  43.         std::cout << std::endl;

  44.         IA<double> *na = a->copy();
  45.         IA<int> *nb = b->copy();
  46.         IA<std::string> *nc = c->copy();
  47.         std::cout << "id: " << na->id() << std::endl;
  48.         std::cout << "data: " << na->GetData() << std::endl;
  49.         std::cout << "id: " << nb->id() << std::endl;
  50.         std::cout << "data: " << nb->GetData() << std::endl;
  51.         std::cout << "id: " << nc->id() << std::endl;
  52.         std::cout << "data: " << nc->GetData() << std::endl;
  53.         delete na;
  54.         delete nb;
  55.         delete nc;

  56.         return 0;
  57. }
复制代码

  1. id: 2660434001472
  2. data: 123.45
  3. id: 2660433999552
  4. data: 123456
  5. id: 2660433984144
  6. data: abcd

  7. id: 2660434001952
  8. data: 123.45
  9. id: 2660434000192
  10. data: 123456
  11. id: 2660433983136
  12. data: abcd
  13. 请按任意键继续. . .
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 12:42:12 | 显示全部楼层
  1. #include <iostream>
  2. #include <sstream>
  3. class A_base
  4. {
  5.     private:
  6.         int a;
  7.     public:
  8.         A_base(const int &b = 0):a(b){}
  9.         void showdata() const
  10.         {
  11.             std::cout << a;
  12.         }
  13.         virtual ~A_base() { std::cout << "调用A_base成功" << std::endl; }
  14. };
  15. template <typename T>
  16. class A : public A_base
  17. {
  18.     private:
  19.         T b;
  20.     public:
  21.         A(const T c = 0) : b(c), A_base(int(c)){}
  22.         void showdata()
  23.         {
  24.             std::cout << "基类数据: ";
  25.             A_base::showdata();
  26.             std::cout << std::endl;
  27.             std::cout << "子类数据: " << b << std::endl;
  28.         }
  29.         ~A() { std::cout << "调用A成功" << std::endl; }
  30. };
  31. int main(int argc, char const *argv[])
  32. {
  33.     A<float> a = 3.014159;
  34.     a.showdata();
  35.     std::cout << std::endl;
  36.     return 0;
  37. }
复制代码

====================================================
Microsoft Windows [版本 10.0.18363.535]
(c) 2019 Microsoft Corporation。保留所有权利。

E:\Users\admin\Documents\VScode\Code>c:\Users\admin\.vscode\extensions\ms-vscode.cpptools-0.26.2\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-vtfeiqkt.yjn --stdout=Microsoft-MIEngine-Out-txf35s5p.zl2 --stderr=Microsoft-MIEngine-Error-w31u0pzy.i0y --pid=Microsoft-MIEngine-Pid-meui1qcz.std --dbgExe=D:\MinGW\bin\gdb.exe --interpreter=mi
基类数据: 3
子类数据: 3.01416

调用A成功
调用A_base成功


E:\Users\admin\Documents\VScode\Code>  
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 13:12:11 | 显示全部楼层
bin554385863 发表于 2020-1-7 12:42
====================================================
Microsoft Windows [版本 10.0.18363.535]
(c) ...

在C++中没有Java中的接口概念,抽象类可以模拟Java中的接口。

用抽象类模拟接口:抽象类中只有纯虚函数,没有成员变量和其他函数。

https://blog.csdn.net/qq_41093451/article/details/79645435

我不记得在哪里看过一句话
“C++中,一个类中如果只有纯虚函数,那么这个类就是接口”
这很可能不是原话,我就是这样理解的


“在C++中,含有纯虚拟函数的类称为抽象类”
也就是说含有纯虚函数的类是抽象类,如果一个类中只有纯虚函数,那就是接口
https://baike.so.com/doc/6213661-6426933.html
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 14:04:35 | 显示全部楼层
我大概还是没说清楚。。
我想要的是这种效果:
  1. IA *p = new A<int>(5);
  2. cout << p->GetData<int>() << endl;       //输出5
  3. p = new A<string>("fishc.com")
  4. cout << p->GetData<string>() << endl;    //输出fishc.com

  5. string s = p->GetData<string>();         //正确赋值
  6. int i = p->GetData<int>();               //报错:string没有默认的转化为int的函数
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 14:15:08 | 显示全部楼层
本帖最后由 Croper 于 2020-1-7 14:17 编辑

中午又想出两个办法,基本解决,但还是感觉有点绕:

方法1,引入一个中间模板类,让A去继承这个中间模板类
  1. #include <string>
  2. #include <iostream>
  3. using namespace std;

  4. //中间类
  5. template <typename T>
  6. struct IA_templ {
  7.         virtual T GetData() = 0;
  8. };


  9. //接口类
  10. struct IA {

  11. public:
  12.         template <typename T>         
  13.         T GetData() {
  14.                 return dynamic_cast<IA_templ<T>*>(this)->GetData();
  15.         }
  16.         virtual ~IA() {};
  17. };


  18. //模板类
  19. template <typename T>
  20. class A :public IA,public IA_templ<T> {   
  21.         T data;  //需要的数据成员
  22. public:
  23.         A(const T& t) :data(t) {};
  24.         T GetData() override {     //这是继承的函数
  25.                 return data;
  26.         }
  27. };

  28. int main() {
  29.         IA *p = new A<int>(5);
  30.         cout << p->GetData<int>() << endl;       //输出5
  31.         p = new A<string>("fishc.com");
  32.         cout << p->GetData<string>() << endl;    //输出fishc.com

  33.         string s = p->GetData<string>();         //正确赋值
  34.         int i = p->GetData<int>();               //报错:string没有默认的转化为int的函数
  35.         system("pause");
  36. }
复制代码

大概符合要求吧,虽然这个接口类已经不是"接口类"了。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 15:06:32 | 显示全部楼层
Croper 发表于 2020-1-7 14:15
中午又想出两个办法,基本解决,但还是感觉有点绕:

方法1,引入一个中间模板类,让A去继承这个中间模板 ...

你为什么非要在GetData后面指定类型?
int i = p->GetData<int>();
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 15:07:47 | 显示全部楼层
Croper 发表于 2020-1-7 14:15
中午又想出两个办法,基本解决,但还是感觉有点绕:

方法1,引入一个中间模板类,让A去继承这个中间模板 ...
  1. p = new A<string>("fishc.com");
复制代码


在创建出这个对象的时候已经指定过了呀

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 15:13:57 | 显示全部楼层
人造人 发表于 2020-1-7 15:06
你为什么非要在GetData后面指定类型?
int i = p->GetData();

因为返回类型不同啊。。C++又没有办法根据返回类型重载。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 15:16:44 | 显示全部楼层
方法二是想自己做虚函数表的。。。。
但是发现成员函数指针的转换不是一般地让人头大。。。研究中。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 15:17:48 | 显示全部楼层
bin554385863 发表于 2020-1-7 12:42
====================================================
Microsoft Windows [版本 10.0.18363.535]
(c) ...

你这根本就不是重载啊- -
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 16:22:31 | 显示全部楼层
Croper 发表于 2020-1-7 15:13
因为返回类型不同啊。。C++又没有办法根据返回类型重载。。。

我明白你的意思了,我研究研究
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 18:06:10 | 显示全部楼层
Croper 发表于 2020-1-7 15:17
你这根本就不是重载啊- -

....你不是只要能访问数据就可以了吗
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 18:17:11 | 显示全部楼层
bin554385863 发表于 2020-1-7 18:06
....你不是只要能访问数据就可以了吗

他在玩设计模式
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 18:19:52 From FishC Mobile | 显示全部楼层
(⊙o⊙)
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 18:31:16 | 显示全部楼层
这样可以吗?
你既然想在GetData后面指定类型,那为什么不能在GetData后面先加.access然后再加类型
GetData后面不能加类型,因为GetData函数不能用模板

写这个代码的时候遇到了一些概念上的问题
  1. class IA
  2. {
  3. public:
  4.         virtual ~IA() = 0;
  5.         virtual uint64_t id() = 0;
  6.         virtual data_t get_data() = 0;
  7. };

  8. // 没办法,子类要求这个函数
  9. IA::~IA()
  10. {
  11. }
复制代码



子类要求IA有一个析构函数,IA中的函数必须都是纯虚函数
子类还要求这个析构函数有实现代码,然后就写成了上面这样
这样的IA还能称得上是接口吗?我百度不到结果

  1. #include <iostream>
  2. #include <string>

  3. class data_t
  4. {
  5. public:
  6.         template <typename T>
  7.         data_t(const T &n, void (*free_pointer)(void *)): value(new T(n)), free_pointer(free_pointer)
  8.         {
  9.         }
  10.         ~data_t()
  11.         {
  12.                 // 当前类不知道这个指针指向的类型,无法删除这个指针,让创建者(创建当前对象的代码)来删除
  13.                 // 我尝试过在当前类中保存指针的类型,但是失败了
  14.                 this->free_pointer(this->value);
  15.         }
  16.         template <typename T>
  17.         T access()
  18.         {
  19.                 return *static_cast<T *>(this->value);
  20.         }

  21. private:
  22.         void *value;
  23.         void (*free_pointer)(void *p);
  24. };

  25. class IA
  26. {
  27. public:
  28.         virtual ~IA() = 0;
  29.         virtual uint64_t id() = 0;
  30.         virtual data_t get_data() = 0;
  31. };

  32. // 没办法,子类要求这个函数
  33. IA::~IA()
  34. {
  35. }

  36. template <typename T>
  37. class A: public IA
  38. {
  39. public:
  40.         A(const T &n): data(n) {}
  41.         uint64_t id()
  42.         {
  43.                 return uint64_t(this);
  44.         }
  45.         data_t get_data()
  46.         {
  47.                 return data_t(data, this->free_pointer);
  48.         }
  49. private:
  50.         T data;

  51.         static void free_pointer(void *p)
  52.         {
  53.                 delete static_cast<T *>(p);
  54.         }
  55. };

  56. int main(void)
  57. {
  58.         IA *p = new A<int>(5);
  59.         std::cout << p->id() << std::endl;
  60.         std::cout << p->get_data().access<int>() << std::endl;

  61.         delete p;
  62.         p = new A<std::string>("fishc.com");
  63.         std::cout << p->id() << std::endl;
  64.         std::cout << p->get_data().access<std::string>() << std::endl;

  65.         std::string s = p->get_data().access<std::string>();
  66.         //int i = p->get_data().access<std::string>();
  67.         delete p;

  68.         return 0;
  69. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-7-4 02:21

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表