使用接口(基类)访问模板类中的数据怎么写才漂亮?
本帖最后由 Croper 于 2020-1-6 21:31 编辑本来以为很简单一个问题。。突然发现我纠结到了。
比如说这里有一个模板类A:template <typename T>
class A{
public:
//....其他数据成员
T data;//某个数据成员
//..其他成员函数
};
为了方便访问它,写了一个接口类,并让模板类继承://接口类
struct IA {
//virtual IA* copy() =0;
//virtual int id() =0;
//...其他接口..blalblabla..
};
//模板类
template <typename T>
class A :public IA {
public:
//....其他数据成员
T data;//某个数据成员
//..其他成员函数
};
这个时候,我想通过IA来访问类A中的数据成员data。
类A是模板类,那么这个接口只能写成模板函数。。。
但是模板函数是不能写成虚函数的,想来想去,只想出两种方法:
//接口类
struct IA {
private:
virtual void* _GetData() = 0;//返回类型改为void*,这样能写成虚函数
public:
template <typename T> //然后再包装一下
T GetData() {
return *static_cast<T*>(_GetData());
}
};
//模板类
template <typename T>
class A :public IA {
public:
T data;//需要的数据成员
void* _GetData() override { //这是继承的函数
return &data;
}
};
这样写的问题是,即使模板函数GetData的类型和模板类不一致且无法转化,也不会报错,
而且类型之间的互相转化也是直接二进制数据强制转化,这肯定不是所期望的
//接口类
struct IA {
public:
template <typename T>
T GetData();
};
//模板类
template <typename T>
class A :public IA {
public:
T data;//需要的数据成员
};
template<typename T>
T IA::GetData()
{
return dynamic_cast<A<T>*>(this)->data;//将this指针转化为A<T>的指针
};
这样首先是不符合设计模式了,抽象类(底层)依赖具体实现(高层)。
另外,同样无法做到double与int的相互转化。
所以还有什么好点的写法么。。?
我仔细想了想,这个问题应该无解,至少我无解
这个问题要求C++能够记住类型信息,也就是要求C++能够正确读取 void *
试想一下,在new一个对象的时候保存这个对象的类型,然后把new出来的对象给void 指针保存,在使用的时候用之前保存的类型信息对void指针解引用
但是C++好像不能读取void *,我百度不到如何保存类型信息
看不懂你想表达什么意思 就是我现在想通过接口类访问模板类中的数据data,
但是找不到一个好的实现方法能满足要求:
1、接受的变量和模板类的类型不匹配且无法相互转化时能报错(就像你给一个string赋一个vector时会报错一样)
2、最好接受的变量和模板类的能互相转化的时候时能正常转化(比如一个double类型的变量能够接受一个整数并隐式转化)
3、符合一般来说面向对象的设计原则。
"类A是模板类,那么这个接口只能写成模板函数。。。
但是模板函数是不能写成虚函数的"
我不明白你说的这句话是什么意思
通过IA来访问类A中的数据成员data
可以在IA中加一个纯虚函数呀,不过IA也要用模板
写成下面这样,是你想要的吗?
#include <iostream>
//接口类
template <typename T>
struct IA {
public:
virtual IA* copy() =0;
virtual uint64_t id() =0;
//...其他接口..blalblabla..
virtual T GetData() = 0;
};
//模板类
template <typename T>
class A:public IA<T> {
public:
IA<T>* copy() {
return new A<T>(*this);
}
uint64_t id() {
return uint64_t(this);
}
T GetData() {
return this->data;
}
//...其他接口..blalblabla..
//....其他数据成员
T data;//某个数据成员
//..其他成员函数
};
int main()
{
A<double> a;
return 0;
}
#include <iostream>
#include <string>
//接口类
template <typename T>
struct IA {
public:
virtual IA* copy() =0;
virtual uint64_t id() =0;
//...其他接口..blalblabla..
virtual T GetData() = 0;
};
//模板类
template <typename T>
class A:public IA<T> {
public:
A(const T &n): data(n) {
}
IA<T>* copy() {
return new A<T>(*this);
}
uint64_t id() {
return uint64_t(this);
}
T GetData() {
return this->data;
}
//...其他接口..blalblabla..
//....其他数据成员
T data;//某个数据成员
//..其他成员函数
};
int main()
{
IA<double> *a = new A<double>(123.45);
IA<int> *b = new A<int>(123456);
IA<std::string> *c = new A<std::string>("abcd");
std::cout << "id: " << a->id() << std::endl;
std::cout << "data: " << a->GetData() << std::endl;
std::cout << "id: " << b->id() << std::endl;
std::cout << "data: " << b->GetData() << std::endl;
std::cout << "id: " << c->id() << std::endl;
std::cout << "data: " << c->GetData() << std::endl;
std::cout << std::endl;
IA<double> *na = a->copy();
IA<int> *nb = b->copy();
IA<std::string> *nc = c->copy();
std::cout << "id: " << na->id() << std::endl;
std::cout << "data: " << na->GetData() << std::endl;
std::cout << "id: " << nb->id() << std::endl;
std::cout << "data: " << nb->GetData() << std::endl;
std::cout << "id: " << nc->id() << std::endl;
std::cout << "data: " << nc->GetData() << std::endl;
delete na;
delete nb;
delete nc;
return 0;
}
id: 2660434001472
data: 123.45
id: 2660433999552
data: 123456
id: 2660433984144
data: abcd
id: 2660434001952
data: 123.45
id: 2660434000192
data: 123456
id: 2660433983136
data: abcd
请按任意键继续. . . #include <iostream>
#include <sstream>
class A_base
{
private:
int a;
public:
A_base(const int &b = 0):a(b){}
void showdata() const
{
std::cout << a;
}
virtual ~A_base() { std::cout << "调用A_base成功" << std::endl; }
};
template <typename T>
class A : public A_base
{
private:
T b;
public:
A(const T c = 0) : b(c), A_base(int(c)){}
void showdata()
{
std::cout << "基类数据: ";
A_base::showdata();
std::cout << std::endl;
std::cout << "子类数据: " << b << std::endl;
}
~A() { std::cout << "调用A成功" << std::endl; }
};
int main(int argc, char const *argv[])
{
A<float> a = 3.014159;
a.showdata();
std::cout << std::endl;
return 0;
}
====================================================
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> 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
我大概还是没说清楚。。
我想要的是这种效果:IA *p = new A<int>(5);
cout << p->GetData<int>() << endl; //输出5
p = new A<string>("fishc.com")
cout << p->GetData<string>() << endl; //输出fishc.com
string s = p->GetData<string>(); //正确赋值
int i = p->GetData<int>(); //报错:string没有默认的转化为int的函数 本帖最后由 Croper 于 2020-1-7 14:17 编辑
中午又想出两个办法,基本解决,但还是感觉有点绕:
方法1,引入一个中间模板类,让A去继承这个中间模板类#include <string>
#include <iostream>
using namespace std;
//中间类
template <typename T>
struct IA_templ {
virtual T GetData() = 0;
};
//接口类
struct IA {
public:
template <typename T>
T GetData() {
return dynamic_cast<IA_templ<T>*>(this)->GetData();
}
virtual ~IA() {};
};
//模板类
template <typename T>
class A :public IA,public IA_templ<T> {
T data;//需要的数据成员
public:
A(const T& t) :data(t) {};
T GetData() override { //这是继承的函数
return data;
}
};
int main() {
IA *p = new A<int>(5);
cout << p->GetData<int>() << endl; //输出5
p = new A<string>("fishc.com");
cout << p->GetData<string>() << endl; //输出fishc.com
string s = p->GetData<string>(); //正确赋值
int i = p->GetData<int>(); //报错:string没有默认的转化为int的函数
system("pause");
}
大概符合要求吧,虽然这个接口类已经不是"接口类"了。。。 Croper 发表于 2020-1-7 14:15
中午又想出两个办法,基本解决,但还是感觉有点绕:
方法1,引入一个中间模板类,让A去继承这个中间模板 ...
你为什么非要在GetData后面指定类型?
int i = p->GetData<int>(); Croper 发表于 2020-1-7 14:15
中午又想出两个办法,基本解决,但还是感觉有点绕:
方法1,引入一个中间模板类,让A去继承这个中间模板 ...
p = new A<string>("fishc.com");
在创建出这个对象的时候已经指定过了呀
人造人 发表于 2020-1-7 15:06
你为什么非要在GetData后面指定类型?
int i = p->GetData();
因为返回类型不同啊。。C++又没有办法根据返回类型重载。。。 方法二是想自己做虚函数表的。。。。
但是发现成员函数指针的转换不是一般地让人头大。。。研究中。。。 bin554385863 发表于 2020-1-7 12:42
====================================================
Microsoft Windows [版本 10.0.18363.535]
(c) ...
你这根本就不是重载啊- - Croper 发表于 2020-1-7 15:13
因为返回类型不同啊。。C++又没有办法根据返回类型重载。。。
我明白你的意思了,我研究研究 Croper 发表于 2020-1-7 15:17
你这根本就不是重载啊- -
....你不是只要能访问数据就可以了吗 bin554385863 发表于 2020-1-7 18:06
....你不是只要能访问数据就可以了吗
他在玩设计模式
(⊙o⊙)
这样可以吗?
你既然想在GetData后面指定类型,那为什么不能在GetData后面先加.access然后再加类型
GetData后面不能加类型,因为GetData函数不能用模板
写这个代码的时候遇到了一些概念上的问题
class IA
{
public:
virtual ~IA() = 0;
virtual uint64_t id() = 0;
virtual data_t get_data() = 0;
};
// 没办法,子类要求这个函数
IA::~IA()
{
}
子类要求IA有一个析构函数,IA中的函数必须都是纯虚函数
子类还要求这个析构函数有实现代码,然后就写成了上面这样
这样的IA还能称得上是接口吗?我百度不到结果
#include <iostream>
#include <string>
class data_t
{
public:
template <typename T>
data_t(const T &n, void (*free_pointer)(void *)): value(new T(n)), free_pointer(free_pointer)
{
}
~data_t()
{
// 当前类不知道这个指针指向的类型,无法删除这个指针,让创建者(创建当前对象的代码)来删除
// 我尝试过在当前类中保存指针的类型,但是失败了
this->free_pointer(this->value);
}
template <typename T>
T access()
{
return *static_cast<T *>(this->value);
}
private:
void *value;
void (*free_pointer)(void *p);
};
class IA
{
public:
virtual ~IA() = 0;
virtual uint64_t id() = 0;
virtual data_t get_data() = 0;
};
// 没办法,子类要求这个函数
IA::~IA()
{
}
template <typename T>
class A: public IA
{
public:
A(const T &n): data(n) {}
uint64_t id()
{
return uint64_t(this);
}
data_t get_data()
{
return data_t(data, this->free_pointer);
}
private:
T data;
static void free_pointer(void *p)
{
delete static_cast<T *>(p);
}
};
int main(void)
{
IA *p = new A<int>(5);
std::cout << p->id() << std::endl;
std::cout << p->get_data().access<int>() << std::endl;
delete p;
p = new A<std::string>("fishc.com");
std::cout << p->id() << std::endl;
std::cout << p->get_data().access<std::string>() << std::endl;
std::string s = p->get_data().access<std::string>();
//int i = p->get_data().access<std::string>();
delete p;
return 0;
}
页:
[1]
2