MFC遇见问题了,不知道如何在界面中创造一个文本框,用于让用户输入文字。而且搜了好久都找不到教程(如果谁会,请联系我,或者加我QQ:20004604帮助我)
因此,只能回头过来更新这个了。回头看了一下十四章的类模板,已经涉及到类模板的继承了,感觉好复杂,似乎也用不到,所以跳过来更新十五章内容了。
十五章计划只更新15.1的友元和15.2的嵌套类。15.3的异常到时候看情况,可能会跳过,然后直接更新十六章内容。
————————————————————————————————
让一个类成为另一个类的友元:
当面临两个类对象,这两个类首先是互相独立且没有共同点的。
因此,不能使用继承(无论是公有还是私有)。
但这两个类之间有联系。例如第一个类的对象,可以操纵并影响第二个类的对象的某些功能(书上的例子是电视机和电视机的遥控器)。
就以书上提供的电视机和遥控器为例,先自行定义类定义:
电视机的(被操控一方):
#include<iostream> //类定义 class TV { bool ison; //开关 int channelmax; //最大频道数 int channel; //当前频道 int volume; //当前音量(最大100,最小0) bool model; //模式,有线1或者无线0 bool AVTV; //AV/TV模式,AV 1,TV 0 public: TV(); //构造函数 void OnOff(); //开关机按钮 void AddChannel(); //当前频道加1 void MinusChannel(); //频道减1 void AddVolume(); //音量加1 void MinusVolume(); //音量减1 void ChangeModel(); //更改有线无线模式 void ChangeAVTV(); //更改AVTV模式 }; //类方法 TV::TV() { ison = false; //开关 channelmax = 10; //刚开始只有10个频道 channel = 1; //初始频道为1 volume = 1; //音量为1 model = 1; //有线 AVTV = 0; //TV模式 } void TV::OnOff() //自定义的时候是分开的,后来想到应该是一个按钮,故合并 { if (ison == true)ison = false; else ison = true; } void TV::AddChannel() { if (ison == false)return; //如果关机则没反应 channel++; if (channel > channelmax)channel = 1; //如果比最大频道大,则跳回初始频道 } void TV::MinusChannel() { if (ison == false)return; //如果关机则没反应 channel--; if (channel > channelmax)channel = channelmax; //如果比最大频道大,则跳回最后频道 } void TV::AddVolume() { if (ison == false)return; //如果关机则没反应 if (volume < 100)volume++; //达到100则没不能再加了 //理论上这里应该还有一个显示音量的画面 } void TV::MinusVolume() { if (ison == false)return; //如果关机则没反应 if (volume > 0)volume--; //理论上这里应该还有一个显示音量的画面 } void TV::ChangeModel() { if (ison == false)return; //如果关机则没反应 if (model == 1)model = 0; else model = 1; } void TV::ChangeAVTV() { if (ison == false)return; //如果关机则没反应 if (AVTV == 1)AVTV = 0; else AVTV = 1; }
然后,因为遥控器的类要求能操控电视机,假设遥控器的类名为Control,因此,需要在TV的public部分加入代码:
public: friend class Control; //使Control成为友元类
于是TV类的完成了,下来制作遥控器类:
class Control //遥控器类 { public: void OnOff(TV&t) { return t.OnOff(); } //开关机按钮 void AddChannel(TV&t) { return t.AddChannel(); } //当前频道加1 void MinusChannel(TV&t) { return t.MinusVolume(); } //频道减1 void AddVolume(TV&t) { return t.AddVolume(); } //音量加1 void MinusVolume(TV&t) { return t.MinusVolume(); } //音量减1 void ChangeModel(TV&t) { return t.ChangeModel(); } //更改有线无线模式 void ChangeAVTV(TV&t) { return t.ChangeAVTV(); } //更改AVTV模式 void SetChannel(int c, TV&t); //选择某个频道,友元类在这个函数里起作用了(可以直接调用私有成员) }; void Control::SetChannel(int c, TV&t) //比最大大则自动为最大,比1小则自动为1 { if (c < 1)c = 1; else if (c>t.channelmax)c = t.channelmax; t.channel = c; }
若在类中声明另一个类为其友元,则另一个类可以使用当前类的私有成员(如在TV中声明Control类为TV类的友元,于是在Control类中的SetChannel()方法中,就可以使用TV类的私有成员channel)。
为了方便展示,故在TV类中再添加一类方法show(),用于输出当前状态(开关,频道,音量,最大频道等:
void TV::show() { using std::cout; using std::endl; cout << "开关:"; if (ison == true)cout << "开"; else cout << "关"; cout << ",最大频道数:" << channelmax << ",当前频道:" << channel << ",当前音量:" << volume << ",有线无线模式:"; if (model == true)cout << "有线"; else cout << "无线"; cout << ",AV/TV:"; if (AVTV == true)cout << "AV"; else cout << "TV"; cout << endl; }
附测试程序
int main() { using namespace std; TV m, n; //两个TV Control one; //遥控器 cout << "输出2个电视(m和n)目前情况" << endl; m.show(); n.show(); cout << "首先开启m和n"; m.OnOff(); n.OnOff(); cout << "设置m的当前频道:"; int p; cin >> p; one.SetChannel(p, m); cout << "设置n的当前频道:"; cin >> p; one.SetChannel(p, n); cout << "m增加5次音量" << endl; for (int i = 0; i < 5; i++) m.AddVolume(); cout << "m增加5次音量,减少1次音量" << endl; for (int i = 0; i < 5; i++) n.AddVolume(); n.MinusVolume(); cout << "m增加3次频道" << endl; for (int i = 0; i < 3; i++) m.AddChannel(); cout << "n减少3次频道" << endl; for (int i = 0; i < 3; i++) n.MinusChannel(); cout << "m切换有线无线模式" << endl; m.ChangeModel(); cout << "n切换AVTV模式" << endl; n.ChangeAVTV(); cout << "重新查看m和n的情况" << endl; m.show(); n.show(); system("pause"); return 0; }
显示:
输出2个电视(m和n)目前情况 开关:关,最大频道数:10,当前频道:1,当前音量:1,有线无线模式:有线,AV/TV:TV 开关:关,最大频道数:10,当前频道:1,当前音量:1,有线无线模式:有线,AV/TV:TV 首先开启m和n设置m的当前频道:10 设置n的当前频道:5 m增加5次音量 m增加5次音量,减少1次音量 m增加3次频道 n减少3次频道 m切换有线无线模式 n切换AVTV模式 重新查看m和n的情况 开关:开,最大频道数:10,当前频道:3,当前音量:6,有线无线模式:无线,AV/TV:TV 开关:开,最大频道数:10,当前频道:2,当前音量:5,有线无线模式:有线,AV/TV:AV 请按任意键继续. . .
让一个类的某个类方法,成为另一个类的友元函数:
让一个类的某个类方法,成为另一个类的友元,和让一个类成为另一个类的友元很相似。
在另一个类中,使用friend 另一个类的函数头。
例如:
friend voidControl::SetChannel(int c, TV&t); //使Control成为友元类
但这样带来一个问题,假如Control类在TV类之前,那么在其类方法中不能使用内联函数(例如voidOnOff(TV&t){ return t.OnOff();} //开关机按钮这样),而且也有一个问题是,Control类中使用了TV类作为参数
如果TV类在Control类之前,那么TV类不知道Control类是什么,自然也没办法把他的某个类方法作为内联函数了。
①为了解决后一个问题,那么应该将Control类放在TV类前面;
②为了让TV类成员(引用)能够作为Control的参数,因此,应该使用前向声明(forward declaraton),即在Control类之前加入语句:class TV;
③但又存在Control类中不能使用内联函数的问题,因此,应该在Control类定义中,不要使用内联函数(但可以在后面类方法定义时,使用inline关键字使其内联),把其函数定义,放于TV类定义之后。
于是,整个结构应该是这样的:
class TV; (类的前向声明)
class Control{类定义};
class TV{ 类定义};
TV类和Control类的类方法定义
这样就可以顺利运行了。
另外,如果相反的话(Control,TV,Control)是不行的。
原因在于,在TV类里面声明Control的某个方法为其友元方法。
因此,在编译的时候,编译器要知道这个方法是什么(即之前声明过),而若只声明某个类,那是不行的(因为编译器只知道有这么一个类,但不知道这个类里面有什么类方法)。
另外,又因为类方法的声明只能在类定义中(类方法的定义可以放其他地方),因此也不能单独把其拿出来,放在类TV的定义之前。
于是,若友元的是类方法,其顺序只能是:
①被友元类的前置声明——》
②友元(包含友元函数的方法所在的)类的声明(不能有内联函数)——》
③被友元类的声明——》
④两个类的类定义
而若友元的是类,顺序则无要求。
代码如下:
//类定义 class TV; //前置,以使用TV类作为参数 class Control //遥控器类 { public: void OnOff(TV&t); //开关机按钮 void AddChannel(TV&t); //当前频道加1 void MinusChannel(TV&t); //频道减1 void AddVolume(TV&t); //音量加1 void MinusVolume(TV&t); //音量减1 void ChangeModel(TV&t); //更改有线无线模式 void ChangeAVTV(TV&t); //更改AVTV模式 void SetChannel(int c, TV&t); //选择某个频道,友元类在这个函数里起作用了(可以直接调用私有成员) }; class TV { bool ison; //开关 int channelmax; //最大频道数 int channel; //当前频道 int volume; //当前音量(最大100,最小0) bool model; //模式,有线1或者无线0 bool AVTV; //AV/TV模式,AV 1,TV 0 public: friend void Control::SetChannel(int c, TV&t); //使Control的类方法成为友元 TV(); //构造函数 void OnOff(); //开关机按钮 void AddChannel(); //当前频道加1 void MinusChannel(); //频道减1 void AddVolume(); //音量加1 void MinusVolume(); //音量减1 void ChangeModel(); //更改有线无线模式 void ChangeAVTV(); //更改AVTV模式 void show(); //输出当前状态 }; //类方法 略。。。
两个类互为友元的情况:
假如Control类中,需要使用TV类的数据成员,我们是已知方法的。
那么在这个的基础上,TV类也要使用Control类的数据成员,则需要做更多。
首先,在两个类的类定义中,添加 friend class 友元类名 这行代码,并且,添加的位置,要位于该类使用友元类对象作为参数的类方法之前。
其次,参数应该是友元类(不过是否能用友元类的某个类方法(使用函数指针)作为参数,不确定。我倾向于是不行的,因为之前没有声明该方法的原型,另外也感觉怪怪的,好像哪里不对)。
最后,类方法要放于两个类定义之后,且类定义中不能使用友元的方法在内联函数中(但是在类定义后的类方法中,可以加关键字inline使用内联函数作为替代)。
如代码:
#include<iostream> //类定义 class M { int a; public: friend class N; //这样要在使用其类的类方法之前 M() :a(0) {} void add(N&n); //N是友元,故能直接引用N作为参数 void show(); }; class N { int b; public: friend class M;//这样要在使用其类的类方法之前 N():b(5){} //默认构造函数,初始值不同 void add(M&m); //M是友元,故能直接引用N作为参数 void show(); }; //类方法定义 void M::add(N&n) { a++; n.b++; } void M::show() //显示其值,也可以将N类对象作为参数传递,用于显示N类对象的数据成员的值 { std::cout << "a:" << a << std::endl; } void N::add(M&m) //两个类的方法,对数值操纵不同,以区分 { m.a += 10; b++; } void N::show() { std::cout << "b:" << b << std::endl; } //测试代码 int main() { using namespace std; M one; N two; one.show(); two.show(); cout << "M对象add方法" << endl; one.add(two); one.show(); two.show(); cout << "N对象add方法" << endl; two.add(one); one.show(); two.show(); system("pause"); return 0; }
运行结果是:
a:0 b:5 M对象add方法 a:1 b:6 N对象add方法 a:11 b:7 请按任意键继续. . .
共同的友元:
假如在一个函数中,需要同时使用两个类的私有成员。
使用一个类的私有成员,有两个办法:
①是该类的类方法(继承是不能直接调用其私有成员的);
②是该类的友元函数。
而这个函数若要使用两个类的私有成员,
首先,①+①的方法是不可能的,因为不可能同时是两个类的类方法。
其次,①+②或者②+①的方法组合,是可以的,上面已经说明了。
最后,②+②的方法,也是可行的。即一个函数,既是第一个类的友元函数,也是第二个类的友元函数。
②+②的方法很简单,两个类的类对象都是其参数,然后在其函数内部使用friend声明其是友元函数(就像使用friend ostream&operator<<(ostream&, 类名& m)这个运算符重载函数一样)。
但由于要在第一个类中,声明友元函数,且友元函数的参数使用第二个类对象(该函数的参数包括第一个类的类对象和第二个类的类对象)。因此,第二个类应该前向声明其类声明。
但在第二个类中就无需这么做了(因为第一个类已经进行定义过了)
如代码:
#include<iostream> class N; class M { int a = 1; public: friend void show(M&m, N&n); }; class N { int b = 2; public: friend void show(M&m, N&n); }; void show(M&m, N&n) { std::cout << "M:" << m.a << ", N:" << n.b << std::endl; } int main() { M a; N b; show(a, b); system("pause"); return 0; }
输出内容如下:
M:1, N:2 请按任意键继续. . .