C++ 防止通过 getter 更改引用
c++ prevent changing reference through getter
我目前正在学习 C++,我是一名经验丰富的 C# 和 Java 开发人员。
我有一个 class B
,其中包含 class A
的成员,我希望 class B
的用户能够更改 class A
中的值,但不能更改 class 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"
}
}
明确禁用值复制!
private:
A(const A&);
A& operator=(const A&);
您可能想使用 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:
的 =operator
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"
}
您写道:
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
还有其他您不想更改的属性,并且赋值会更改它们,那么与其公开整个 A
object via B::getA()
只需添加一个新的成员函数到 B
来设置名称。这样做比简单地暴露整个 A
对象更好的封装。 Java 和 C# 似乎常常鼓励涉及 getter 和 setter 的糟糕设计,这是毫无意义的,也没有封装任何东西;如果所有内容都有 setter,您不妨让每个成员 public 并直接访问它们。
如果你想要一个 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;
};
当您允许一般非 const
访问成员(例如 A B::anInstance
)时,这意味着可以访问该成员的所有(public
)成员(const
而不是)。特别是,您提供对 operator=
的访问权限,这允许更改成员的 内容 。当然还是原来的A
(地址不变),只是数据变了。
在您的 C# 代码中,您实际上永远不会 allowing/using 非 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
的任何访问权限,但 returns 只是对其的一个副本。通常(如果 A
是非平凡的 and/or 大)这比仅仅返回一个引用(如指针)需要更多的努力,但就 usage/effect 而言,它非常类似于getA() const
(如果 A
的某些 const
成员实际上改变了 A
的状态,或者如果你使用可怕的 const_cast<>
就像 const_cast<A&>(b.getA())=otherA
).
我目前正在学习 C++,我是一名经验丰富的 C# 和 Java 开发人员。
我有一个 class B
,其中包含 class A
的成员,我希望 class B
的用户能够更改 class A
中的值,但不能更改 class 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"
}
}
明确禁用值复制!
private:
A(const A&);
A& operator=(const A&);
您可能想使用 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:
的 =operatorclass 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"
}
您写道:
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
还有其他您不想更改的属性,并且赋值会更改它们,那么与其公开整个 A
object via B::getA()
只需添加一个新的成员函数到 B
来设置名称。这样做比简单地暴露整个 A
对象更好的封装。 Java 和 C# 似乎常常鼓励涉及 getter 和 setter 的糟糕设计,这是毫无意义的,也没有封装任何东西;如果所有内容都有 setter,您不妨让每个成员 public 并直接访问它们。
如果你想要一个 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;
};
当您允许一般非 const
访问成员(例如 A B::anInstance
)时,这意味着可以访问该成员的所有(public
)成员(const
而不是)。特别是,您提供对 operator=
的访问权限,这允许更改成员的 内容 。当然还是原来的A
(地址不变),只是数据变了。
在您的 C# 代码中,您实际上永远不会 allowing/using 非 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
的任何访问权限,但 returns 只是对其的一个副本。通常(如果 A
是非平凡的 and/or 大)这比仅仅返回一个引用(如指针)需要更多的努力,但就 usage/effect 而言,它非常类似于getA() const
(如果 A
的某些 const
成员实际上改变了 A
的状态,或者如果你使用可怕的 const_cast<>
就像 const_cast<A&>(b.getA())=otherA
).