C++ 尽可能灵活地使用泛型数据类型的指针、引用和句柄
在我的应用程序中,我有很多不同的数据类型,例如汽车、自行车、人。。。(它们实际上是其他数据类型,但这只是示例) 因为我的应用程序中也有相当多的“通用”代码,而且应用程序最初是用C编写的,指向汽车、自行车、人的指针。。。通常作为无效指针传递给这些通用模块,并带有类型标识,如下所示:C++ 尽可能灵活地使用泛型数据类型的指针、引用和句柄,c++,architecture,generic-programming,C++,Architecture,Generic Programming,在我的应用程序中,我有很多不同的数据类型,例如汽车、自行车、人。。。(它们实际上是其他数据类型,但这只是示例) 因为我的应用程序中也有相当多的“通用”代码,而且应用程序最初是用C编写的,指向汽车、自行车、人的指针。。。通常作为无效指针传递给这些通用模块,并带有类型标识,如下所示: Car myCar; ShowNiceDialog ((void *)&myCar, DATATYPE_CAR); “ShowNiceDialog”方法现在使用元信息(将数据类型映射到接口以从车中获取实际数据
Car myCar;
ShowNiceDialog ((void *)&myCar, DATATYPE_CAR);
“ShowNiceDialog”方法现在使用元信息(将数据类型映射到接口以从车中获取实际数据的函数)根据给定的数据类型获取车的信息。这样,泛型逻辑只需编写一次,而不是针对每种新的数据类型每次都编写一次
当然,在C++中,使用一个普通的根类,比如,可以使这一点变得简单多了。
class RootClass
{
public:
string getName() const = 0;
};
class Car : public RootClass
{
...
};
void ShowNiceDialog (RootClass *root);
问题是,在某些情况下,我们不希望将数据类型存储在类中,而是以完全不同的格式存储以节省内存。
在某些情况下,我们需要在应用程序中管理数亿个实例,我们不想为每个实例创建一个完整的类。
假设我们有一个具有2个特征的数据类型:
- 数量(双精度,8字节)
- 布尔值(1字节)
任何与此相关的框架、白皮书、研究材料的引用?在这种情况下,听起来您应该简单地使用重载。例如:
#ifdef __cplusplus // Only enable this awesome thing for C++:
# define PROVIDE_OVERLOAD(CLASS,TYPE) \
inline void ShowNiceDialog(const CLASS& obj){ \
ShowNiceDialog(static_cast<void*>(&obj),TYPE); \
}
PROVIDE_OVERLOAD(Car,DATATYPE_CAR)
PROVIDE_OVERLOAD(Bicycle,DATATYPE_BICYCLE)
// ...
#undef PROVIDE_OVERLOAD // undefine it so that we don't pollute with macros
#endif // end C++ only
如果更改了c
的类型,则它仍将使用适当的重载(如果没有重载,则会给出错误)。这并不能阻止用户使用现有的类型不安全的C变量,但由于类型安全的版本更容易调用,我希望其他开发人员也会喜欢它
编辑我应该补充一点,上面回答的问题是如何使API类型安全,而不是如何使实现类型安全。这将帮助使用您的系统的用户避免不安全的调用。还请注意,这些包装器为使用编译时已知的类型提供了一种类型安全的方法。。。对于动态类型,确实有必要使用不安全的版本。但是,另一种可能性是您可以提供如下所示的包装器类:
Car c;
// ...
ShowNiceDialog(c);
class DynamicObject
{
public:
DynamicObject(void* data, int id) : _datatype_id(id), _datatype_data(data) {}
// ...
void showNiceDialog()const{ ShowNiceDialog(_datatype_data,_datatype_id); }
// ...
private:
int _datatype_id;
void* _datatype_data;
};
对于这些动态类型,在构建对象时仍然没有太多安全性,但一旦构建了对象,就有了更安全的机制。将其与类型安全工厂相结合是合理的,这样API的用户就不会自己实际构造DynamicObject类,因此就不需要调用不安全的构造函数。我会使用traits
template <class T>
struct DataTypeTraits
{
};
template <>
struct DataTypeTraits<Car>
{
// put things that describe Car here
// Example: Give the type a name
static std::string getTypeName()
{
return "Car";
}
};
template <>
struct DataTypeTraits<Bicycle>
{
// the same for bicycles
static std::string getTypeName()
{
return "Bicycle";
}
};
template <class T>
ShowNiceDialog(const T& t)
{
// Extract details of given object
std::string typeName(DataTypeTraits<T>::getTypeName());
// more stuff
}
模板
结构数据类型特征
{
};
模板
结构数据类型特征
{
//把描述汽车的东西放在这里
//示例:为类型指定一个名称
静态std::string getTypeName()
{
归还“汽车”;
}
};
模板
结构数据类型特征
{
//自行车也一样
静态std::string getTypeName()
{
归还“自行车”;
}
};
模板
显示对话框(常量T&T)
{
//提取给定对象的详细信息
std::string typeName(DataTypeTraits::getTypeName());
//更多的东西
}
这样,无论何时添加要应用的新类型,都不需要更改ShowNiceDialog()。您只需要为新类型专门化DataTypeTraits。如果您不想要一个完整的类,您应该仔细阅读模式。它是为了节省内存而设计的 编辑:抱歉,午餐时间暂停;) 典型的FlyWeight方法是将大量对象共有的特性与给定实例的典型特性分开 一般来说,它意味着:
struct Light
{
kind_type mKind;
specific1 m1;
specific2 m2;
};
kind\u type
通常是指针,但不是必需的。在您的例子中,这将是一个真正的浪费,因为指针本身将是“有用”信息的4倍大
在这里,我认为我们可以利用填充来存储id。毕竟,正如您所说,它将扩展到16位,即使我们只使用其中的9位,所以我们不要浪费其他7位
struct Object
{
double quantity;
bool flag;
unsigned char const id;
};
请注意,元素的顺序很重要:
0x00 0x01 0x02 0x03
[ ][ ][ ][ ]
quantity flag id
0x00 0x01 0x02 0x03
[ ][ ][ ][ ]
id flag quantity
0x00 0x02 0x04
[ ][ ][ ][ ][ ][ ]
id -- quantity flag --
我不理解“运行时扩展”这一点。看起来很可怕。这是某种自我修改的代码吗
模板允许创建一个非常有趣的FlyWeight形式:
可储存在以下容器中:
typedef std::vector<types_t> vector_t;
第二种方法看起来很奇怪,但它意味着你可以以一种最有趣的方式使用它,作为谓词:
vector_t vec = /**/;
std::foreach(vec.begin(), vec.end(), boost::apply_visitor(DoSomething()));
仔细阅读变体,它是最有趣的
- 编译时检查:您错过了一个
?编译器抛出操作符()
- 不需要RTTI:没有虚拟指针,没有动态类型-->与使用union一样快,但安全性更高
当然,您可以通过定义多个变体来分割代码。如果代码的某些部分只处理4/5类型,那么就为它使用一个特定的变量:)完全可以在s中更改类的打包
typedef std::vector<types_t> vector_t;
struct DoSomething: boost::static_visitor<>
{
void operator()(Dog const& dog) const;
void operator()(Car const& car) const;
void operator()(Cycle const& cycle) const;
void operator()(GenericVehicle const& vehicle) const;
template <class T>
void operator()(T const&) {}
};
types_t t;
boost::apply_visitor(DoSomething(), t);
// or
boost::apply_visitor(DoSomething())(t);
vector_t vec = /**/;
std::foreach(vec.begin(), vec.end(), boost::apply_visitor(DoSomething()));
class VehicleBase {
public:
virtual std::string GetCarOwnerFirstName() = 0;
virtual ~VehicleBase();
};
class Car : public VehicleBase {
int index;
public:
std::string GetCarOwnerFirstName() { return GetSingleton().carownerfirstnames[index]; }
};