c ++防止通过getter更改引用
Posted
技术标签:
【中文标题】c ++防止通过getter更改引用【英文标题】:c++ prevent changing reference through getter 【发布时间】:2015-03-13 15:16:44 【问题描述】:我目前正在学习 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"
【问题讨论】:
你知道const
吗?
string getName()
应该是 const string& getName() const
(这只有在返回成员变量时才可以)和 setName(string value)
应该是 setName(const string& value)
这会减少字符串的副本。
他在问题中特别说“我希望B类的用户能够修改A类”。这不是关于 const,而是关于他的假设,即分配给调用者的引用也会改变 B 中的引用,但它不会。不要那么粗鲁和傲慢。
允许调用者修改 A 但不允许从另一个实例分配 A 是相当矛盾的,因为后者是前者的特例。您可以在 A 类上禁用 operator=
。
@JfBeaulac 简而言之,b.getA() = anotherA;
并没有像你想象的那样做。
【参考方案1】:
你写道:
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
。换句话说,您已将名称更改为 say "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,这是没有意义的,也没有封装任何东西;如果所有内容都有设置器,您不妨将每个成员公开并直接访问它们。
如果你想要一个 B
包含一个除了名称之外不会改变的 A
,那么不要暴露整个 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;
;
【讨论】:
【参考方案2】:当您允许一般非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
,即只允许const
的A
成员访问。第二个getA()
只能从非const
B
访问,并允许完全(public
)访问anInstance
,包括A::operator=(A const&)
(如果存在,即在定义中声明或未声明=delete
class A
)。
最后,copyA()
不提供对B::anInstance
的任何访问权限,而只返回一个副本。通常(如果A
不重要和/或很大)这比仅仅返回一个引用(如指针)需要更多的努力,但在使用/效果方面,它与getA() const
非常相似(不同如果A
的某些const
成员实际上改变了A
的状态,或者如果您使用可怕的const_cast<>
,如const_cast<A&>(b.getA())=otherA
)。
【讨论】:
【参考方案3】:您可能希望使用 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"
【讨论】:
您的示例不起作用,因为anInstance
从未初始化。
@NeilKirk 它不起作用,因为它没有编译。您可以根据需要初始化 A*。问题不在于这个。
如果您给出一个看起来可以编译和运行的示例,它应该可以成功运行,或者您应该明确声明它无法运行。
@NeilKirk PS 寻找一种方法使此代码无法编译。你在说什么?
Captain Price 的回答并不建议禁止赋值运算符。【参考方案4】:
明确禁用值复制!
private:
A(const A&);
A& operator=(const A&);
【讨论】:
如何允许赋值运算符被调用并表现出异常行为,帮助?以上是关于c ++防止通过getter更改引用的主要内容,如果未能解决你的问题,请参考以下文章