关于C++类中的数据域封装。求好人帮忙
发布网友
发布时间:2022-04-24 08:01
我来回答
共5个回答
热心网友
时间:2022-04-22 09:23
[yidetion]说的完全正确,就举他说的这个例子吧,例子有点长,但浅显易懂:
1、====================-
假如有个类Circle
class Circle
{
public:
bool setRadius(double nRadius) //设置半径的接口,给用户代码用的
{
if( !(m_Radius<=0) ) {
m_Radius=nRadius;
return true;
}else{
return false;
}
}
public:
//public标号表示数据不被封装,任何类的用户都可以直接访问这个数据成员m_Radius
unsingned double m_Radius; //半径
}
2、====================-
假如有一个人使用了这个类Circle,在它的main函数里面有如下代码:
int main()
{
Circle c;
c.m_Radius = -1;
}
代码解释:因为数据成员没有封装,所有可以直接使用类的对象用‘.’操作符访问它的成员【c.m_Radius】,然后因为没有任何*,我可以将半径为负值,即【c.m_Radius= -1】;很明显这样做是错误的。其实Circle提供了一个正确的使用方法就是一有setRadius成员函数,它能保证m_Radius改变它且保证不为负值。
3、====================-
假如有一天Circle类的创造者突然觉得m_Radius这个名字太土了,想换一个,换成m_2b。
问题来了,使用这个类的用户会怎么样呢?例如上面:
main函数里面的【c.m_Radius】编译时报错,会说Circle类里面没有一个名字叫做“m_Radius”的数据成员。如果用户在上万个地方使用了【c.m_Radius】,那麻烦大了,用户得一个一个改,改成【c.m_2b】。
总结:上面这些现象就是没有封装数据成员普遍造成的问题,想要彻底的解决问题就是——“封装”,将半径访问标识改为private:
class Circle
{
public:
bool setRadius(double nRadius) //设置半径的接口,给用户代码用的
{
if( !(m_Radius<=0) ) {
m_Radius=nRadius;
return true;
}else{
return false;
}
}
private: <——————————修改部分
unsingned doublem_Radius; //半径
}
这样,类的用户就不能直接放类的数据成员了,即上面的【c.m_Radius】是错误的,即使m_Radius没有被改名字。要修改半径的值,用户只要调用改半径的函数,c.setRadius(100),并且这个函数是安全的(因为它保证只有用户给的值大于0才会修改半径)。如果类的创建者想改变m_Radius的名字只需要改这个setRadius里面的名字,我们用户不用管创建者把半径名字改成了什么。因为我们没有(也不能)直接使用m_Radius这个名字,即没有(也不能有)c.m_Radius之类的代码。<——这就是封装的好处。
所有用户正确且安全的做法是通过setRadius成员函数修改半径:
int main()
{
Circle c;
bool rul;
rul = c.setRadius(111); //修改会成功,rul会等于true
rul = c.setRadius(-5); //修改会失败,rul会等于false
}
热心网友
时间:2022-04-22 10:41
1.如果你设计的类封装了数据,只提供了操作这些数据的接口,那么客户在编写程序的时候只是使用你的类提供的接口。哪天你想改进你的类,或者对数据进行不同的操作,只要你提供的接口不变,那么随便你怎么改,客户写的程序是不用修改的。
2.
假如你不封装数据,即
class Circle
{
public:
int radius;
};
比如客户写的程序如下:
int main()
{
Circle c1;
c1.radius = -5;
return 0;
}
那如果现在要radius保持非负,那就得改main函数里边 这就是客户写的程序。如果现在客户已经写了很多有关于radius的代码,那就很难修改了。
如果封装数据:
class Circle
{
public:
void input(int r);
private:
int radius;
};
void Circle::input(int r)
{
radius = r;
}
这样的话,客户用你的类,要改变radius得值,就只能使用你提供的接口,即input。
int main()
{
Circle c1;
c1.input(-5);
return 0;
}
现在你想让radius保持非负数,你只修改你自己的类。
void Circle::input(int r)
{
radius = abs(r); //对r取绝对值。
}
客户的代码就根本不用修改了。不是很方便么。
面向对象编程的好处远不仅如此,好好加油吧,你会体会到其中的乐趣的。
纯手工,希望能帮到你。让我们一起在编程之路上前行吧~~
热心网友
时间:2022-04-22 12:15
1. 类的抽象从现实意义来说的,比如,我跟你不认识,我不会让你碰我的手机对吧。现在你就想玩我的手机,你说我该怎么办?打你一顿。在程序里一样的。如果是一个对象,那么除非特殊的理由,否则我们应该把数据封装为私有成员。好处,维护简单。为什么维护简单。因为我们在操作
radius都会用set_radius等。如果哪天你告诉我,设置前要做判断。好我们在set_radius()中先判断。而外部的代码不用改!明白了吗?如果不是这样的话,你就要在你每个Circle1.radius =-5前面都要加一句if(`````)对不对?
2.如果这个类已经在用并且没有为扩展有所保留,那么你必须重新修改这个类。这不明摆着的嘛。你改了一个类,所有引用到这个类的地方都得检查,这是必要的吧?如果在一个DLL中导出了Circle这个类,那么客户程序也一定要改。所有调用处需要手工检查,就是这样。追问你说“如果不是这样的话,你就要在你每个Circle1.radius =-5前面都要加一句if(`````)对不对?”
是这样吗?
double r;
Circle1.radius =(r〉=0)?r:0;
而写成类函数
void Circle::setRadius(double newRadius)
{
radius = (newRadius>=0)?newRadius:0;
}
热心网友
时间:2022-04-22 14:07
1、不封装的话,你的数据别人可以随意修改,你不知道他怎么改的,改成什么样子。而你要对类的功能进行修改时,也要修改用到这些功能的客户程序,所以你维护起来就很难。
2,、因为使用Circle类的程序可能修改了Circle类的radius数据,而这种修改可能使radius变成负值;所以你要使radius保持非负值,除了要修改Circle类,还要修改使用到它的程序。
如果你对Circle的radius数据进行了封装,即客户程序只能通过类提供的接口函数来修改radius数据,那么你要实现radius保持非负值的功能时,只要在类提供的接口程序里面加以*,使radius保持非负就可以了,就不用再去修改客户程序。追问亲,你关于问题一的阐述,能帮忙举个例子吗?
追答楼下已经有例子了。
热心网友
时间:2022-04-22 16:15
这书是不是官方用语太多了吃饱了撑的啊。我不知道是否理解对了,可以和你分享下我的理解。
我用c++到现在没听说过数据域。看上面描述,感觉应该说的是类里成员的访问权限。也就是我们用的public那几个关键字。假设是这么个概念,那么请往下看,如果不是,那么就不要看了。
问题1、public属性的成员,可以直接访问和修改。假设我现在要对当前radius做一个修改。比方说,曾经用到radius这个类成员的地方全部要*50。那么你需要将所有用到Circle类里radius这个成员的地方找出来,然后手动修改。当然,你也可以搜索替换,可如果有个Circle2的类里面也有个radius呢?这将会引起错误。所以他的意思是说,类成员,最好不要直接访问和使用。而是将他们封装成类函数,通过函数去获取或修改。这样,你下次遇到要修改的时候,就可以直接修改函数,而不需要去查找之后再修改。
问题2、radius保持非负值。那么我需要做的可能就是,在radius加上unsigned修饰。这需要修改头文件(这是radius这个变量的实现和声明),然后重新编译成需要的文件(exe、dll等客户程序)。才能在新版的执行程序中使用到修改过后的功能。
虽然不知道对不对。但是感觉问题2的原文描述很扯,有什么办法在修改了代码之后能不重新编译代码就能使用到新版本的方法么?追问我看的那本书上说:“一个对象的状态,用数据域(data fields,也称为属性)及它们当前值来表示。”这本书上说的所谓的数据域就是一个对象的属性。
你对问题一的描述,我觉得挺有道理,让我想起了命名常量。“最好不要直接访问和使用。而是将他们封装成类函数,通过函数去获取或修改。这样,你下次遇到要修改的时候,就可以直接修改函数,而不需要去查找之后再修改。”有道理。
谢谢啦。