我一直想在STL的字符串中使用更多功能。由于子类化STL类型是不是没有,大多数情况下我已经看到推荐的扩展这些类的方法只是编写将类型作为第一个参数的函数(而不是成员函数)。
我从来没有对这个解决方案感到兴奋。首先,并不一定明显所有这些方法都在代码中,另一方面,我只是不喜欢语法。我想用。当我打电话给方法时!
前一段时间我想出了以下内容:
class StringBox
{
public:
StringBox( std::string& storage ) :
_storage( storage )
{
}
// Methods I wish std::string had...
void Format();
void Split();
double ToDouble();
void Join(); // etc...
private:
StringBox();
std::string& _storage;
};
请注意,StringBox需要对std :: string进行构造的引用...这对它的使用提出了一些有趣的限制(我希望,这意味着它不会导致字符串类扩散问题)...在我自己的代码,我几乎总是只是在一个方法中在堆栈上声明它,只是为了修改一个std :: string。
使用示例可能如下所示:
string OperateOnString( float num, string a, string b )
{
string nameS;
StringBox name( nameS );
name.Format( "%f-%s-%s", num, a.c_str(), b.c_str() );
return nameS;
}
我的问题是:StackOverflow社区的C ++专家对这种STL扩展方法有何看法?
答案 0 :(得分:19)
我从来没有对这个解决方案感到兴奋。首先,并不一定明显所有这些方法都在代码中,另一方面,我只是不喜欢语法。我想用。当我打电话给方法时!
我想在调用方法时使用$!---&
!处理它。如果您要编写C ++代码,请坚持使用C ++约定。一个非常重要的C ++约定是在可能的情况下更喜欢非成员函数。
C ++大师推荐这个有一个原因:
它改进了封装,可扩展性和重用。 (std::sort
可以使用所有迭代器对,因为它不是任何单个迭代器或容器类的成员。无论你如何扩展std::string
,你只要您坚持使用非成员函数,就不能打破它。即使您无法访问或不允许修改类的源代码,您仍然可以扩展它通过定义非成员函数)
就个人而言,我无法在代码中看到这一点。这不是更简单,更可读和更短吗?
string OperateOnString( float num, string a, string b )
{
string nameS;
Format(nameS, "%f-%s-%s", num, a.c_str(), b.c_str() );
return nameS;
}
// or even better, if `Format` is made to return the string it creates, instead of taking it as a parameter
string OperateOnString( float num, string a, string b )
{
return Format("%f-%s-%s", num, a.c_str(), b.c_str() );
}
在罗马时,就像罗马人一样,正如俗话所说的那样。 特别是当罗马人有充分的理由去做的时候。特别是当你的拥有的方式实际上并没有单一的优势时。它更容易出错,让阅读代码的人感到困惑,非惯用,只需更多行代码即可。
至于你的问题,很难找到扩展string
的非成员函数,如果这是一个问题,请将它们放在命名空间中。这就是他们的目的。创建namespace StringUtil
或其他内容,然后将其放在那里。
答案 1 :(得分:15)
由于我们大多数“大师”似乎赞成使用可能包含在命名空间中的自由函数,我认为可以肯定地说你的解决方案不会受欢迎。我担心我看不到它有一个单一的优势,而且这个类包含一个参考的事实是邀请它成为一个悬垂的参考。
答案 2 :(得分:2)
我会添加一些尚未发布的内容。 Boost String Algorithms library采用了自由模板函数方法,它们提供的字符串算法对于看起来像字符串的任何内容都非常可重用:std :: string,char *,std :: vector,iterator pairs ..你说出来的名字!并且他们将它们整齐地放在boost :: algorithm命名空间中(我经常使用using namespace algo = boost::algorithm
来使字符串操作代码更简洁)。
因此,请考虑使用免费模板函数进行字符串扩展,并查看Boost字符串算法,了解如何使它们“通用”。
要获得安全的printf样式格式,请查看Boost.Format。它可以输出到字符串和流。
我也希望一切都成为会员功能,但我现在开始看到光明了。 UML和doxygen总是迫使我把函数放在类中,因为我被C ++ API ==类层次结构的想法所洗脑。
答案 3 :(得分:1)
如果字符串的范围与StringBox
不同,则可以获得段错误:
StringBox foo() {
string s("abc");
return StringBox(s);
}
至少通过声明赋值运算符并复制ctor private来防止对象复制:
class StringBox {
//...
private:
void operator=(const StringBox&);
StringBox(const StringBox&);
};
编辑:关于API,为了防止意外,我会让StringBox
拥有该字符串的副本。我可以想到两种方法:
std::tr1::shared_ptr
或boost:shared_ptr
)访问您的字符串,以防止额外复制答案 4 :(得分:1)
功能松散的问题在于它们功能松散。
我敢打赌,你们大多数人已经创建了一个已经由STL提供的功能,因为你根本不知道存在STL功能,或者它可以做你想要完成的事情。
这是一个相当严厉的设计,特别是对于新用户。 (STL也有新的补充,进一步增加了问题。)
Google:C ++ to string
提到了多少结果:std :: to_string
我很有可能找到一些古老的C方法,或者一些自制版本,因为我要找到任何给定函数的STL版本。
我更喜欢成员方法,因为您不必费力地找到它们,并且您不必担心找到旧的弃用版本等。 (即string.SomeMethod,几乎可以肯定是你应该使用的方法,它为你提供了一些具体的东西。)
C#样式扩展方法将是一个很好的解决方案。
这应该允许每个人完全按照自己的意愿行事。
似乎可以在IDE本身完成,而不需要任何语言更改。
基本上,如果解释器遇到一个不存在的成员的调用,它可以检查标头是否匹配松散函数,并在将其传递给编译器之前动态修复它。
在加载智能感知数据时可以做类似的事情。
我不知道这对于现有函数是如何工作的,不应该轻易做出这样的大规模更改,但是,对于使用新语法的新函数,它应该不是问题。
namespace StringExt
{
std::string MyFunc(this std::string source);
}
可以单独使用,也可以作为std :: string的成员使用,IDE可以处理所有繁琐的工作。
当然,这仍然存在方法分散在各种标题上的问题,这可以通过各种方式解决。
在没有引起问题的情况下解决这个问题更加困难......
答案 5 :(得分:0)
如果你想扩展可用于字符串的方法,我会通过创建一个具有静态方法的类来扩展它,该方法将标准字符串作为参数。 这样,人们可以自由使用您的实用程序,但不需要更改其功能的签名来获取新类。
这会稍微破坏面向对象的模型,但会使代码更加健壮 - 即如果更改字符串类,则对其他代码的影响不大。
遵循建议的指导方针,原因如下:)
答案 6 :(得分:0)
最好的方法是使用模板化的自由函数。接下来最好的是私有继承struct extended_str : private string
,它恰好在C ++ 0x中变得更容易,就像using
构造函数一样。私有继承太麻烦,风险太大,只是添加了一些算法。你正在做的事情对任何事情来说风险太大。
您刚刚介绍了一个非常重要的数据结构来完成代码标点符号的更改。您必须为每个string
手动创建和销毁一个Box,您仍然需要将您的方法与本地方法区分开来。你很快就厌倦了这个惯例。