C++ 防止通过 getter 更改引用
c++ prevent changing reference through getter
我目前正在学习c++,我是一个经验丰富的C#和Java开发人员。
我有一个类B
,其中包含类A
的成员,我希望类B
的用户能够更改类A
中的值,但不能更改类A
本身的实例。
基本上,它想防止b.getA() = anotherA;
被允许。
这在 c++ 中可能吗,还是我的设计在这里完全错误?
这是我的小 c++ 程序
#include <string>
#include <iostream>
using namespace std;
class A {
public:
string getName()
{
return name;
}
void setName(string value)
{
name = value;
}
private:
string name = "default";
};
class B {
public:
A &getA()
{
return anInstance;
}
private:
A anInstance;
};
int main(int argc, char** argv) {
B b;
cout << b.getA().getName() << std::endl; // outputs "default"
b.getA().setName("not default");
cout << b.getA().getName() << std::endl; // outputs "not default"
A a;
a.setName("another a instance");
b.getA() = a; // I want to prevent this being possible
cout << b.getA().getName() << std::endl; // outputs "another a instance"
}
我正在尝试执行的操作的 C# 示例
class Program
{
class A
{
private string name = "default";
public string getName()
{
return name;
}
public void setName(string value)
{
name = value;
}
}
class B
{
private A anInstance;
public A getA()
{
return anInstance;
}
}
static void Main(string[] args)
{
B b = new B();
Console.WriteLine(b.getA().getName()); // outputs "default"
b.getA().setName("not default");
Console.WriteLine(b.getA().getName()); // outputs "not default"
}
}
你写道:
A a;
a.setName("another a instance");
b.getA() = a; // I want to prevent this being possible
我问,为什么?
你为什么要防止这种情况?
您的下一行是:
cout << b.getA().getName() << std::endl; // outputs "another a instance"
但这具有误导性,您没有更改b
内部A
的实例,您只是将b.anInstance
更改为a
的副本。换句话说,你已经把名字改成了"another a instance"
但这并不意味着它是真的。这是另一个实例,而不是你调用b.getA().setName("another a instance")
(事实上,结果与这样做相同!
试试吧:
A a;
a.setName("another a instance");
std::cout << &b.getA() << std::endl;
b.getA() = a;
std::cout << &b.getA() << std::endl;
两次你都会打印相同的地址,因为b.getA() = a
不会取代b.anInstance
,它只是修改它,就像打电话setName
一样。
这是因为在C++ B::anInstance
中不仅仅是对A
的引用,它是一个A
,所以通过分配给它,你不会改变引用指向不同的A
,你改变了A
本身。
所以,回到你最初的问题,既然你担心的事情无论如何都不会发生,你为什么需要阻止它? 如果您已经允许通过setName()
函数修改b.anInstance
,为什么不让赋值也对其进行修改呢?
如果这个问题的答案是A
还有其他属性你不想被更改,并且赋值会改变它们,那么不是通过B::getA()
公开整个A
对象,只需添加一个新的成员函数来设置名称B
。这样做比简单地公开整个A
对象更好。Java 和 C# 似乎经常鼓励糟糕的设计,涉及所有内容的 getter 和 setter,这是毫无意义的,什么都没有封装;您不妨将每个成员都公开,如果所有成员都有设置器,则直接访问它们。
如果你想要一个包含除名称外不会更改的A
的B
,则不要公开整个A
,只需在外部对象上提供一个 setter:
class A {
public:
string getName() const // N.B. Added 'const' here
{
return name;
}
void setName(string value)
{
name = value;
}
private:
string name = "default";
};
class B {
public:
// Read-only access to the contained object:
const A& getA() const
{
return anInstance;
}
// Update the name of the contained object:
void setName(string value)
{
anInstance.name = value;
}
private:
A anInstance;
};
当你允许对一个成员进行一般的非const
访问(例如A B::anInstance
)时,这意味着可以访问该成员的所有(public
)成员(const
和不)。特别是,您提供对 operator=
的访问,这允许更改成员的内容。当然,它仍然是相同的A
(具有相同的地址),但其数据已更改。
在你的C#代码中,你实际上永远不会允许/使用非const
访问B::anInstance
,所以你的C++并不真正等同。考虑
class B
{
A anInstance; // private by default
public:
A const& getA() const { return anInstance; } // const access only
A & getA() { return anInstance; } // full access
A copyA() const { return anInstance; } // make a copy
};
第一个getA()
可以从const B
访问,并且只允许const
访问anInstance
,即只允许A
的const
成员。第二getA()
只能从非const
B
访问,并允许完全(public
)访问anInstance
,包括A::operator=(A const&)
(如果存在,即在class A
的定义中声明或不=delete
)。
最后,copyA()
不提供对B::anInstance
的访问,而只返回一个副本。通常(如果A
是非平凡的和/或大的),这比仅仅返回引用(如指针)需要更多的努力,但就用法/效果而言,它与getA() const
非常相似(如果某些const
A
成员实际上改变了A
的状态,或者如果您使用可怕的const_cast<>
,则有所不同const_cast<A&>(b.getA())=otherA
)。
您可能希望使用 const 指针而不是引用,但它可能会产生很多副作用:
class A {
public:
string getName() const
{
return name;
}
void setName(string value)
{
name = value;
}
private:
string name;
};
class B {
public:
A * const getA() const
{
return anInstance;
}
private:
A* anInstance;
};
int main(int argc, char** argv) {
B b;
cout << b.getA()->getName() << std::endl; // outputs "default"
b.getA()->setName("not default");
cout << b.getA()->getName() << std::endl; // outputs "not default"
A a;
a.setName("another a instance");
b.getA() = a; // I want to prevent this being possible
cout << b.getA()->getName() << std::endl; // outputs "another a instance"
}
正如@Captain Price提到的,你可以禁止=A类的运算符:
class A {
public:
string getName()
{
return name;
}
void setName(string value)
{
name = value;
}
private:
string name;
A& operator=(const A& other);
};
class B {
public:
A &getA()
{
return anInstance;
}
private:
A anInstance;
};
int main(int argc, char** argv) {
B b;
cout << b.getA().getName() << std::endl; // outputs "default"
b.getA().setName("not default");
cout << b.getA().getName() << std::endl; // outputs "not default"
A a;
a.setName("another a instance");
b.getA() = a; // I want to prevent this being possible
cout << b.getA().getName() << std::endl; // outputs "another a instance"
}
显式禁用值复制!
private:
A(const A&);
A& operator=(const A&);
- 将对象数组的引用传递给函数
- 什么时候在C++中返回常量引用是个好主意
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 何时在引用或唯一指针上使用移动语义
- 如何在c++中使用引用实现类似python的行为
- 编译C++时未定义的引用
- 返回常量对象引用 (getter) 和仅返回字符串有什么区别?
- 对类成员而不是 getter 的 Const 引用
- 使用const_cast const-ref getter 实现引用获取器的陷阱
- 为什么将指针转换为引用在我的访问器 (getter) 中不起作用?
- 此getter是否返回由构造函数或对班内对象的引用给出的引用?C
- C++ 防止通过 getter 更改引用
- 引用代替 getter
- 我可以使用常量引用而不是 getter 函数吗?
- C++重载getter两次,一次返回指针,另一次返回常量引用,都失败了
- 使用getter函数传递引用参数时出现意外行为
- 通过引用返回或创建一个典型的setter/getter
- 代理对象/引用getter与setter
- getter方法中的Const引用
- C++getter临时与常量引用