C++ 我应该创建一个类而不是重复的get/set语句吗?
考虑以下代码(这是原始代码的简化版本): 这是我的问题,在所有的课程中都有重复!假设我有10个类似于C++ 我应该创建一个类而不是重复的get/set语句吗?,c++,class,optimization,accessor,C++,Class,Optimization,Accessor,考虑以下代码(这是原始代码的简化版本): 这是我的问题,在所有的课程中都有重复!假设我有10个类似于CType的类,那么应该有20个=2*10的表达式,就像上面的例子一样!我不想在所有20个代码位置重复类似的代码!它错了 我认为我应该定义一个新的类类型: class my::Identifier { private: std::string name; public: explicit Identifier(std::string name) : name{std:
CType
的类,那么应该有20个=2*10的表达式,就像上面的例子一样!我不想在所有20个代码位置重复类似的代码!它错了
我认为我应该定义一个新的类类型:
class my::Identifier
{
private:
std::string name;
public:
explicit Identifier(std::string name)
: name{std::move(RequireIdentifier(name))} // Duplicate Code
{
}
std::string get() const
{
return name;
}
void set(std::string value)
{
name = std::move(RequireIdentifier(value)); // Duplicate Code
}
};
然后将CType
类的定义更改为以下代码:
class my::CType
{
private:
Identifier simple_name;
public:
explicit CType(Identifier simple_name)
: simple_name{std::move(simple_name)}
{
}
Identifier get_simple_name() const
{
return simple_name;
}
void set_simple_name(Identifier value)
{
simple_name = std::move(value);
}
};
inline std::string& my::RequestIdentifier(std::string& value)
{
for(char c : value)
{
if(!isalnum(c) && c != '_')
{
throw std::invalid_argument{""};
}
}
return value;
}
inline std::string& my::RequireIdentifier(std::string& value) // HERE IS IT
{
if(value.empty())
{
throw std::invalid_argument{""};
}
else
{
return my::RequestIdentifier(value);
}
}
顺便说一句,我无法避免重复的std::move
部分,这不是问题
但是如果我没有重载标识符
类的=
操作符,并且如果我没有为标识符
类声明到std::string
的隐式转换,那么标识符
类将不会作为字符串
类工作。那么这不是一个糟糕的设计选择吗?在这种情况下,避免重复代码的正确方法是什么(好吧,我们不应该从任何STL容器中生成子类)
编辑:将RequireIdentifier
功能定义为以下代码:
class my::CType
{
private:
Identifier simple_name;
public:
explicit CType(Identifier simple_name)
: simple_name{std::move(simple_name)}
{
}
Identifier get_simple_name() const
{
return simple_name;
}
void set_simple_name(Identifier value)
{
simple_name = std::move(value);
}
};
inline std::string& my::RequestIdentifier(std::string& value)
{
for(char c : value)
{
if(!isalnum(c) && c != '_')
{
throw std::invalid_argument{""};
}
}
return value;
}
inline std::string& my::RequireIdentifier(std::string& value) // HERE IS IT
{
if(value.empty())
{
throw std::invalid_argument{""};
}
else
{
return my::RequestIdentifier(value);
}
}
结论
您可以看到,如果我将基础字符串的“提取”放在我的CType
类中(正如Tony D在接受的答案中提到的),如果我想使用string
类型,然后,我必须在我的10个类中重复调用Identifier
类的get
和set
成员函数,就像我调用RequireIdentifier
函数一样。因此,从技术上讲,创建一个类只是为了消除一个函数调用冗余不是一个好方法
另一方面,不管代码是否冗余,如果我真的认为string
类型不能很好地表示它是一种新类型,我应该声明Identifer
类,并且我可以为它声明一个隐式的用户定义构造函数,使它与string
类型兼容,最后,标识符
类的目的不是成为字符串
类型的获取
和设置
访问器,它的目的是成为一个新类型
但是如果我没有重载Identifier类的=操作符,如果我没有为Identifier类声明对std::string的隐式转换,那么Identifier类将不会作为string类工作。那么这不是一个糟糕的设计选择吗?在这种情况下,避免重复代码的正确方法是什么
所以。。。只需将基础字符串的“提取”放在CType
类中:
class my::CType
{
private:
Identifier simple_name;
public:
explicit CType(Identifier simple_name)
: simple_name{std::move(simple_name)}
{ }
std::string get_simple_name() const
{
return simple_name.get();
}
void set_simple_name(std::string value)
{
simple_name.set(std::move(value));
}
};
更新:根据下面评论中的要求-使用check\u invariants()
函数的说明。仍然有一些冗余,但您可以重复任意数量的检查,而无需进一步修改每个变异函数
class my::CType
{
private:
std::string simple_name;
void check_invariants()
{
if (!is_identifier(simple_name))
throw std::invalid_argument("empty identifier);
}
public:
explicit CType(std::string simple_name)
: simple_name{std::move(simple_name))
{
check_invariants();
}
std::string get_simple_name() const
{
return simple_name;
}
void set_simple_name(std::string value)
{
simple_name = std::move(value);
check_invariants();
}
};
…与
bool is_identifier(const std::string& s)
{
return !s.empty() &&
(isalpha(s[0]) || s[0] == '_') &&
std::all_of(s.begin() + 1, s.end(), [](char c) { return isalnum(c) || c == '_'; });
}
RequireIdentifier
的原因函数是什么?这似乎有点奇怪,它返回的东西,然后你试图移动。如果两次从同一个左值移动,会发生什么?@juanchopanza:它的定义是为了避免重复检查字符串是否为空。我包括了它的定义。一个带有纯set/get函数的类被过度设计了,仅仅一个struct就足够了吗?还是一个字符串就足够了?@billz:这是我的问题,我需要检查字符串是否为空,不能只允许一个可以为空的字符串@ccsadegh只需为其编写一个自由函数就足够了?您是否鼓励为重复的get/set语句定义一个新类?@ccsadegh这些都是一个平衡问题。。。定义一个类来执行这样的不变量通常是合理的,尽管消除一行冗余并不令人信服。一个值得注意的替代方法是添加一个check_invariants()
函数,您可以在构造函数和赋值运算符的末尾调用该函数,该函数可以检查任意数量的此类条件。此外,get
和set
通常表示一种“反模式”-可能是这样的名称可能是存储值的映射中的键或许多其他模型中的键-如果不了解您的应用程序,很难知道…@ccsadegh:这甚至不是必需的,因为在检查不变量之前,成员变量仍然可以在初始化列表中进行初始化。@ccsadegh我感谢您通过编辑我的进一步意见来创建一个您满意的答案,但您确实应该发布自己的答案或制作自己的摘要,作为对问题的编辑。事实上,我的名字和我没有写过的内容是一致的,也不一定同意。编辑的重点应放在纠正明显和重大的意外错误或遗漏,添加指向已提及内容的链接,修复损坏的格式,使内容看起来像清晰的预期内容。或者,当你有观点要发表时,你应该把它们作为评论,让我决定是否编辑它们。@ccsadegh:不用担心——你写的任何具体内容都不会让我烦恼——只是一个“如此和谐”的提示:-。干杯