我想提出一个“基于混合蛋白的结构模式”(这甚至是一个术语吗?),但我不确定它是否会在“某些情况下”出现。
基本思想是生成乘以继承mixins的“使用模板类的类型”。所以类型声明看起来像:typedef BaseType<Mixin1, Mixin2, MixinN> Type1;
该方法的一些成就:
Type1
的特殊功能(如运算符重载和构造函数重载)始终可用。BaseType
提取。通常的模板混合方法形式here如下:template<class Base> class Printing : public Base {...}
。这种方法对我的主要缺点:
Printing
强制转换为Base
以使用Base
的某些特殊功能,或者必须显式提供这些重载(我知道这只是一个问题一行代码)。但是在某些情况下,这会很烦人。这就是为什么我想出了产生基础的想法。 请看一下实现(“某些情况”):
#include <iostream>
#include <functional>
#ifdef QT_CORE_LIB
#include <QString>
#endif
template<template<class> class... mixin_t>
class StringType : public mixin_t<StringType<mixin_t...>>...
{
std::string _value;
public:
StringType() : _value("") {}
StringType(const StringType &other) = default; // Copy
StringType(StringType &&other) = default; // Move
#ifdef QT_CORE_LIB
StringType(const QString &value) { this->_value = value.toStdString(); }
#endif
StringType(const std::string &value) { _value = value; }
StringType(const char *value) { _value = value; }
template<template<class> class T>
StringType(const StringType<T> &value)
{
_value = static_cast<const std::string &>(value);
}
StringType &operator=(const StringType &rhs) = default; // copy assign
StringType &operator=(StringType &&rhs) = default; // Move assign
#ifdef QT_CORE_LIB
operator QString() const { return QString::fromStdString(_value);}
#endif
operator std::string() const { return _value; }
operator const char *() const{ return _value.c_str(); }
};
template<class this_t> struct _empty_mixn {};
template<class this_t> struct ToStringMixin
{
this_t toString() const { return *static_cast<const this_t *>(this); }
};
template<class this_t> struct StringPrinterMixin
{
void print() const
{
std::cout << "From the printer: " << *static_cast<const this_t *>(this);
}
};
typedef StringType<_empty_mixn> String;
typedef StringType<ToStringMixin> Message;
typedef StringType<ToStringMixin, StringPrinterMixin> PrinterAttachedString;
int main()
{
Message msg1(String("msg1\n"));
std::cout << msg1;
std::cout << "toString() : " << msg1.toString();
Message msg2 = String("msg2\n");
std::cout << msg2;
std::cout << "toString() : " << msg2.toString();
Message msg3(std::string("msg3\n"));
std::cout << msg3;
std::cout << "toString() : " << msg3.toString();
Message msg4 = std::string("msg4\n");
std::cout << msg4;
std::cout << "toString() : " << msg4.toString();
Message msg5("msg5\n");
std::cout << msg5;
std::cout << "toString() : " << msg5.toString();
Message msg6 = "msg6\n";
std::cout << msg6;
std::cout << "toString() : " << msg6.toString();
std::cout << "\n---------------------\n\n";
PrinterAttachedString str1(String("str1\n"));
std::cout << str1;
std::cout << "toString() : " << str1.toString();
str1.print();
PrinterAttachedString str2 = String("str2\n");
std::cout << str2;
std::cout << "toString() : " << str2.toString();
str2.print();
PrinterAttachedString str3(std::string("str3\n"));
std::cout << str3;
std::cout << "toString() : " << str3.toString();
str3.print();
PrinterAttachedString str4 = std::string("str4\n");
std::cout << str4;
std::cout << "toString() : " << str4.toString();
str4.print();
PrinterAttachedString str5("str5\n");
std::cout << str5;
std::cout << "toString() : " << str5.toString();
str5.print();
PrinterAttachedString str6 = "str6\n";
std::cout << str6;
std::cout << "toString() : " << str6.toString();
str6.print();
return 0;
}
所以,我的问题:
编辑:针对Phil1970的回答:
我将从问题3的答案开始。
StringType
相当final
的事实,因此不适用。并且StringType
不知道或不是真正的混合类。 * 1 现在回答第1个问题。
final
,剩下的事就可以了。 * 2 之前的问题消失了(非常感谢Phil),提出了新的问题。
StringStyle
不依赖于mixin,我认为没有理由这样做。当然,如果有必要,它可以使用私有头。那么如何执行耦合?非常感谢。
答案 0 :(得分:-1)
您的问题:
std::string
运算符,例如+,+ =。包装类除了为您提供更多的转换外没有任何其他好处,因为您随后将使用新的字符串类型并使用mixin方法,这甚至更糟,因为您还需要在自己的类型之间进行转换。对于上述简单功能,首选的方法是定义一个名称空间(或者,如果您有很多可以通过某种方式进行分类的功能(例如,文件名操作),则可以命名),然后在其中包含自由功能。
通过使用名称空间,您具有一些优点:
如果某些原始mixin保持状态,那么您应该执行一个辅助类。对于诸如HTML构建器之类的类可能就是这种情况,该类可能具有可用于创建HTML文档的功能,例如AddTag,Add Attribute,AddEncodedUrl等。
此方法的一大优点是,耦合比设计中的宽松得多。例如,一个文件对(标题和源)将包含用于打印机的所有功能。如果需要,您不必创建使用混合混合的新类。
您的方法的一个大问题是,随着时间的流逝,您将有很多不同的StringType<…>
如果您可以使用5个mixin,则您有2 ^ 5 = 32个类。到那时,几乎可以肯定的是,您经常会需要不包含的mixin,如果调用得很深,您会得到级联的更改。而且,如果您在所有地方都使用模板,则编译速度会变慢,并且可能会使代码膨胀。
在大多数情况下,大多数专家也认为,最好避免隐式转换。如果您在多个类之间进行了多次转换,那么在某些时候您将产生意想不到的转换或歧义。明确进行某些转换可以限制该问题。通常最好使用显式转换,就像std::string
中的专家所做的那样。如果要使用C样式字符串,则必须调用成员函数c_str()
。
例如,由于您的StringType
类同时定义了对const char *
和QString
的转换,因此,如果您有一个接受两者的方法(可能是一个Append函数),则您有一个冲突。
如果您确实要进行转换,请改用命名方法(例如AsQString()
,c_str()
,tostdstring()
...)。它有助于确保进行所有转换。这样可以更轻松地找到它们,并且像在代码中的某些位置所做的那样,最好进行显式强制转换。尽管static_cast
和其他强制类型转换有时很有用,但是在重构代码时也可能隐藏一些问题,因为在某些情况下,强制类型转换可能会在编译时不正确。如果您强制转换为派生类,并且在某个时候决定将派生类更改为其他内容而忘记更新某些强制类型,则会是这种情况。
您应该为应用程序选择最合适的字符串,并在需要时进行转换。在大型应用程序中,您可能在UI中使用一种类型(例如CString或QString),而在跨平台或与第三方库共享的库中使用标准字符串。有时这些库也有自己的字符串类。您的选择应尽量减少无用的转换。