强制const对象的C++类
C++ class that mandates const objects
首先让我问一个一般性问题:
- 在我看来,C++是一种语言,它的目的是提供大量的功能,让程序员在以他认为合适的方式做事时有最大的灵活性。换句话说,这是一种灵活的语言,它允许程序员用比我见过的任何其他语言都更多的方式来表达自己。这是对的吗
然后是一个特定的:
- 为什么C++似乎不支持创建对象只能是
const
的类?换句话说,如果你试图创建一个非const
对象,就会在类中出现编译时错误。在很多情况下,这似乎是一件很自然的事情,但C++不允许这样做。这背后的原因是什么?(当然,我可以让所有成员成为const
,但这有点像是一种逃避。)
为什么C++似乎不支持创建对象只能是常量的类?换句话说,如果你试图创建一个非常量对象,就会在类中出现编译时错误。在很多情况下,这似乎是一件很自然的事情,但C++不允许这样做。这背后的原因是什么?
首先想到的问题是,当拥有一个所有对象都是const
的类型时,这是一件很自然的事情。有些类型是自然不可变的,在某些语言中,甚至建议尽可能多地使类型不可变,但这与强制所有对象都是const
不同。
在类设计中,控制是否有任何成员函数修改对象的状态以实现不可变对象是很简单的。封装会发挥它的魔力,如果你不公开任何变异函数,那么类型是不可变的,和const
一样好。您也可以(如果您想要额外的安全性)将所有成员数据限定为const
,以便编译器告诉您是否在自己的代码中违反了自己的约束。请注意,强制所有对象隐式为const
会将所有成员数据标记为const
,并且您的非常数函数无效。
既然高级效果可以通过不同的方式以很低的成本实现,为什么你会让一种复杂的语言变得更加复杂而没有附加值?
虽然上面的问题看似花言巧语,但事实并非如此。考虑扩展该语言的标准之一是投资回报:如果没有它,我们能用这个功能做什么?(也就是说,这是一个启用功能吗?)如果答案是否定的,那么下一个问题是,与当前状态相比,有了这个功能,程序员的生活会简单多少?(对于非启用功能,添加它们的价值是什么?)
在启用类别中,示例为右值引用;如果没有该语言功能,就无法实现完美转发,也无法实现移动语义。在第二类中,您可以考虑lambdas—没有lambdas,就没有什么是不可行的;其价值在于简化用户代码(必须复杂的代码:创建一个函子,声明正确类型的所有成员,提供一个初始化的构造函数—捕获成员-hellip;),但使用lambdas,代码会变得简单得多。请注意,Sutter花了很多时间才说服Stroustrup lambdas有价值。
在您的情况下,您所要求的不是一个启用功能,并且在没有该功能的情况下支持用例的成本很低,因此无法将其作为核心语言功能提供。
您的第一段详细介绍了C++是如何灵活的,并允许您表达许多不同的程序。
你的第二段问,为什么C++不允许程序员强制执行一个非常严格的限制,而这个限制的好处值得怀疑(当你可以简单地标记所有成员const
以获得几乎相同的结果时)。
让我们每个人从这种二分法中得出自己的结论
C++语言设计的关键在于它提供的抽象成本非常低(通常为零)。使用类并不比在C中提供结构和相关函数更昂贵,但编译器可以对代码实施限制,从而实现类的语义优势。模板、引用、lambdas、复制/移动语义、字符串、I/O等也是如此。你不会为不使用的东西付费,而且当你使用某些东西时,你只需支付很少的费用。
C++的另一个重要设计原则是,它不提供无法从其他语言功能轻松构建的语言功能,也不为作者提供有用的抽象形式。每个语言功能都是一个构建块,可以用来制作一个完全表达的程序。这有助于灵活性。如果该语言要提供您想要的功能,就会降低灵活性。如果希望对象的状态为const
,而不管客户端希望如何使用它,则需要使对象的每个成员都为const
——这些成员就是对象的状态。C++不想给你任何东西;它只是为您提供了必要的工具,使您能够以最低的成本编写出具有充分表达能力的代码。
只需将类的实例创建为const
。这将确保所有内部构件均为const
。
例如const MY_TYPE obj;
我遇到过几次这种情况。也就是说,我设计了一个类,由于不可接受的性能要求,它基本上是不可变的。不可变的,因为它的实例可以在构造时定义,而永远不会修改。实际上,它的所有成员功能都是const
。(好吧,还有一个次要的问题,它是否仍然可以分配,但让我们继续)。
如果能够强制使用声明实例const
,以使意图更加明显,那就太好了。
我认为C++在不添加间接级别的情况下不支持这一点,即通过常量指针访问对象并禁用直接构造。(或者更好的是unique_ptr
)。
struct A{ // this class is immutable, so in princple `A a` and `A const a` should behave the same
int f() const{return 5;}
private:
A(){}
public:
template<class... Args>
static A const* make_const(Args&&... args){return new A(std::forward<Args>(args)...);}
};
int main(){
std::unique_ptr<A const> a(A::make_const());
std::cout << a->f() << std::endl;
}
请注意,以下内容未按预期编译:
A a; // doesn't compile because the constructors are private
也没有
std::unique<A> a(A::make_const()); // make creates pointers to const, so this doesn't compile.
只有以下编译,并且实际上您正在强制执行const
:
std::unique_ptr<A const> a(A::make());
使用std::indirect
可以使它变得更好http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0201r1.pdf并且在未来过载CCD_ 21也将有所帮助。
当然,一个更便宜的替代方案是给出类的名称,在名称中有const
,如struct A_const{...
或struct cA{...
,以暗示只有const
是有意义的。(也许,如果您以后决定拥有可变成员,您可以继承struct A : A_const{...
)。
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- CMake-按正确顺序将项目与C运行时对象文件链接
- 空基优化子对象的地址
- 将对象数组的引用传递给函数
- 你能重载对象变量名本身返回的内容吗
- C++使用整数的压缩数组初始化对象
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 将对象移动到std::shared_ptr
- 代理对象的常量正确性
- 提升 ASIO 无法识别计时器对象
- 将Ref对象作为类成员
- 将包含C样式数组的对象初始化为成员变量(C++)
- 如何返回一个类的两个对象相加的结果
- 使用std::函数映射对象方法
- 是否需要删除包含对象的"pair"?
- 如何在自删除后将对象设置为nullptr
- 迭代时从向量和内存中删除对象
- 构造对象的歧义
- 使用"std::unordereded_map"映射到"std::list"对象