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
请按任意键继续. . .