鱼C论坛

 找回密码
 立即注册
12
返回列表 发新帖
楼主: Croper

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

[复制链接]
 楼主| 发表于 2020-1-7 19:38:31 | 显示全部楼层
子类要求IA有一个析构函数,IA中的函数必须都是纯虚函数
子类还要求这个析构函数有实现代码,然后就写成了上面这样
这样的IA还能称得上是接口吗?我百度不到结果

其实我觉得,一个类只要是不含任何数据成员,无任何构造函数,有virtual函数,都能算是接口类。甚至不需要是抽象类。
应为C++提到接口类,一般是多继承的时候,所谓”一个基类,多个接口类“。
这种要求是为防止多继承过程中出现的混乱,主要也就是构造函数的混乱以及菱形继承中数据成员重复继承的问题。

另一方面来说,接口中写两个函数对于返回的东西做一点包装也是很合理也很合逻辑的吧(汽车修理员口吻),
所以为什么一定要是全纯虚函数才能叫接口类呢,语法上又没有明确的规定,用着不会出问题就行。

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

使用道具 举报

 楼主| 发表于 2020-1-7 19:48:10 | 显示全部楼层
嗯。。你这个似乎不需要吧data_t写这么复杂。。。
就是把指向数据的void*指针包装一下就好:
  1. class data_t
  2. {
  3. public:
  4.         template <typename T>
  5.         data_t(T& p) :value(&p) {};

  6.         template <typename T>
  7.         T access()
  8.         {
  9.                 return *static_cast<T *>(this->value);
  10.         }

  11. private:
  12.         void *value;
  13. };

  14. class IA
  15. {
  16. public:
  17.         virtual ~IA() {};
  18.         virtual uint64_t id() = 0;
  19.         virtual data_t get_data() = 0;
  20. };


  21. template <typename T>
  22. class A : public IA
  23. {
  24. public:
  25.         A(const T &n) : data(n) {}
  26.         uint64_t id()
  27.         {
  28.                 return uint64_t(this);
  29.         }
  30.         data_t get_data()
  31.         {
  32.                 return data_t(data);
  33.         }
  34. private:
  35.         T data;
  36. };

  37. int main(void)
  38. {
  39.         IA *p = new A<int>(5);
  40.         std::cout << p->id() << std::endl;
  41.         std::cout << p->get_data().access<int>() << std::endl;

  42.         delete p;
  43.         p = new A<std::string>("fishc.com");
  44.         std::cout << p->id() << std::endl;
  45.         std::cout << p->get_data().access<std::string>() << std::endl;

  46.         std::string s = p->get_data().access<std::string>();
  47.         //int i = p->get_data().access<std::string>();
  48.         delete p;

  49.         system("pause");
  50. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 19:51:37 | 显示全部楼层
。。。感觉还是这段代码的再包装啊。。
  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. };
复制代码

仍然有数据类型不匹配的时候不报错而是给出诡异结果的问题。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 20:07:35 | 显示全部楼层
中午那段代码大概能实现要求,但是数据类型必须精确匹配。不然dynamic_cast直接转化出nullptr然后报错。。

目标是实现兼容的数据能自动转化,不兼容的数据能报错,大概就像这样:
  1.         IA *p = new A<int>(5);
  2.         cout << p->get_data<int> << endl;   //输出5
  3.         cout << p->get_data<double> << endl; //输出5.00000
  4.         cout << p->get_data<string> << endl; //错误
复制代码


真是不好弄。。如果不靠枚举,if else的话。。
大概就是中午那个是1对1的,而这种是多对多的。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 21:44:31 | 显示全部楼层
Croper 发表于 2020-1-7 19:38
其实我觉得,一个类只要是不含任何数据成员,无任何构造函数,有virtual函数,都能算是接口类。甚至不需 ...

那更像一个包含函数的结构体把
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 22:10:05 | 显示全部楼层
Croper 发表于 2020-1-7 20:07
中午那段代码大概能实现要求,但是数据类型必须精确匹配。不然dynamic_cast直接转化出nullptr然后报错。。
...

你中午的那个代码也不行,你现在不是要一个函数返回多个类型,而是要进行类型转换
我也没有好的办法,我再研究研究

  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.         cout << p->GetData<double>() << endl; //输出5.00000          // 直接崩溃退出
  32.         cout << p->GetData<string>() << endl; //错误
  33.         system("pause");
  34. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 23:00:39 | 显示全部楼层
本帖最后由 Croper 于 2020-1-7 23:02 编辑

你说的大概是RTTI,运行时类型判断。
C++的RTTI大概只有两种,typeid和dynamic_cast。

至于你说的new一个对象时保存对象的类型,通过保存type_info确实可以保存对象类型,但是无法在运行时通过type_info反向得到类型。。。

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;

  4. struct Base {
  5.         virtual ~Base() {};
  6. };

  7. struct Deliver1:public Base {

  8. };
  9. struct Deliver2:public Base {

  10. };
  11. struct Deliver3:public Base {

  12. };

  13. int main() {
  14.         Base* A = new Deliver1;
  15.         Base* B = new Deliver2;
  16.         Base* C = new Deliver3;
  17.         cout << typeid(*A).name()<< endl; //输出 struct Deliver1
  18.         cout << typeid(*B).name() << endl; //输出 struct Deliver2
  19.         cout << typeid(*C).name() << endl; //输出 struct Deliver3
  20.         system("pause");
  21. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 23:07:33 | 显示全部楼层
也就是说你说的
这个问题要求C++能够记住类型信息,也就是要求C++能够正确读取 void *
试想一下,在new一个对象的时候保存这个对象的类型,然后把new出来的对象给void 指针保存,

到这里都是能做到的

但是
在使用的时候用之前保存的类型信息对void指针解引用

好像并没有什么好的解决方法
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 23:14:12 | 显示全部楼层
对于通过不同的数据值反推出类型,我只知道编译时决定的:
  1. #include <iostream>
  2. #include <string>
  3. using namespace std;

  4. template <int N>
  5. struct GetType;

  6. template <>
  7. struct GetType<1> {
  8.         using Type = int;
  9. };

  10. template <>
  11. struct GetType<2> {
  12.         using Type = double;
  13. };

  14. template <>
  15. struct GetType<3> {
  16.         using Type = string;
  17. };

  18. int main() {
  19.         constexpr int a = 2 + 1;     //但这是编译时的,要求参数是constexpr的,最大的可能性也就是这样用了
  20.         GetType<a>::Type sz = "12345";  //sz的类型是string
  21. }
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 23:21:06 | 显示全部楼层
Croper 发表于 2020-1-7 23:14
对于通过不同的数据值反推出类型,我只知道编译时决定的:

嗯,所以我认为你这个问题无解,至少我无解
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 23:25:17 | 显示全部楼层
Croper 发表于 2020-1-7 23:00
你说的大概是RTTI,运行时类型判断。
C++的RTTI大概只有两种,typeid和dynamic_cast。

嗯,这只是输出类型信息,要如何保存起来呢?保存起来留到之后使用

既然没办法用这个类型信息,那保存起来也没有意义了吧
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2020-1-7 23:28:48 | 显示全部楼层
本帖最后由 Croper 于 2020-1-7 23:30 编辑

想到一个办法。。理论上应该可行,,但是实际上
天杀的C++并不让你在函数参数中使用constexpr

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;

  4. template <typename T>
  5. struct A :public IA {
  6.         T mydata;
  7.         void* _GetData(constexpr int a) {
  8.                 return new GetType<a>::Type(mydata);    //通过类型信息反推类型
  9.         }
  10. };

  11. struct IA {
  12.         virtual void* _GetData(constexpr int b); //但是天杀的C++并不让你在形参里使用constexpr。。。虽然从头到尾都是能在编译时推出的。。
  13. public:
  14.         template <typename T>
  15.         T GetData() {
  16.                 constexpr size_t a = typeid(T).hashcode();
  17.                 void* p= _GetData(a);
  18.                 T ret = *(T*)p;
  19.                 delete *(T*)p;
  20.                 return *(T*)_GetData(a);  //传递类型信息
  21.         }
  22.         virtual ~IA() {};
  23. };
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2020-1-7 23:40:34 | 显示全部楼层
Croper 发表于 2020-1-7 23:28
想到一个办法。。理论上应该可行,,但是实际上
天杀的C++并不让你在函数参数中使用constexpr

嗯,如果你还不想放弃,还想继续研究研究,那你继续吧,我就不继续了,因为这个问题在我的所学范围内无解
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

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

本版积分规则

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

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

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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