关于CArchive类中三个友元函数的疑惑?!

i_liangwei 2011-01-09 12:45:18
说明:以下所贴代码来自VC6自带的MFC版本(应该是4.2吧);

大致在AFX.H文件的1789行位置左右,有如下所示代码:

public:
// Object I/O is pointer based to avoid added construction overhead.
// Use the Serialize member function directly for embedded objects.
friend CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb);

friend CArchive& AFXAPI operator>>(CArchive& ar, CObject*& pOb);
friend CArchive& AFXAPI operator>>(CArchive& ar, const CObject*& pOb);

// insertion operations
CArchive& operator<<(BYTE by);
CArchive& operator<<(WORD w);

大致在AFX.INL文件的413行位置左右,有上述声明的具体实现:

_AFX_INLINE CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb)
{ ar.WriteObject(pOb); return ar; }
_AFX_INLINE CArchive& AFXAPI operator>>(CArchive& ar, CObject*& pOb)
{ pOb = ar.ReadObject(NULL); return ar; }
_AFX_INLINE CArchive& AFXAPI operator>>(CArchive& ar, const CObject*& pOb)
{ pOb = ar.ReadObject(NULL); return ar; }

我疑惑的地方是,上述三个操作符为什么需要设定友元,实现代码中好像没用到CArchive类的私有属性或方法,即使用到,直接把这三个操作符作为成员函数不行吗?(妄高手帮忙解惑下!)
...全文
209 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
i_liangwei 2011-01-14
  • 打赏
  • 举报
回复
没人知道为什么吗?坐等高手...

[Quote=引用 9 楼 tomhe666 的回复:]

上面那个段码有点问题,
a.push_back(5);
a.push_back(6);
改为
a.push_back(A());
a.push_back(A());
就可以了, 这个不是重点, 因为代码是从我的一段程序中拷出来的,未经测试, 只是为了说明问题
原本想在贴子上直接改的, 发现我没这个权限,有点晕
[/Quote]
hztj2005 2011-01-12
  • 打赏
  • 举报
回复
在百度百科中:
http://baike.baidu.com/view/534170.htm

如果想重载operator>>和operator<<来读写string对象,你会很快发现它们不能是成员函数。如果是成员函数的话,调用它们时就必须把string对象放在它们的左边:
// 一个不正确地将operator>>和
// operator<<作为成员函数的类
class string {
public:
string(const char *value);
...
istream& operator>>(istream& input);
ostream& operator<<(ostream& output);
private:
char *data;
};
string s;
s >> cin; // 合法, 但有违常规
s << cout; // 同上
这会把别人弄糊涂。所以这些函数不能是成员函数。注意这种情况和前面的不同。这里的目标是自然的调用语法,前面关心的是隐式类型转换。
正确用法
istream& operator>>(istream& input, string& string)
{
delete [] string.data;
read from input into some memory, and make string.data
point to it
return input;
}
ostream& operator<<(ostream& output,
const string& string)
{
return output << string.data;
}
注意上面两个函数都要访问string类的data成员,而这个成员是私有(private)的。但我们已经知道,这个函数一定要是非成员函数。这样,就别无选择了:需要访问非公有成员的非成员函数只能是类的友元函数。
tomhe666 2011-01-12
  • 打赏
  • 举报
回复
上面那个段码有点问题,
a.push_back(5);
a.push_back(6);
改为
a.push_back(A());
a.push_back(A());
就可以了, 这个不是重点, 因为代码是从我的一段程序中拷出来的,未经测试, 只是为了说明问题
原本想在贴子上直接改的, 发现我没这个权限,有点晕

tomhe666 2011-01-12
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 arong1234 的回复:]
个人觉得这里应该直接用成员函数(我不觉得友元更灵活,不知道所谓的灵活性来源于哪里)不要以为微软的就一定有非常充足的原因,这里也许只是程序员临时犯傻
[/Quote]

//==============================================
用友元的好处我还说不清,我遇到一个情况, == 操作符不是友元函数时通不过编译, 具体如下代码
#include "stdafx.h"
#include "win32.h"

#include<string>
#include<vector>
#include<iostream>
//#include"boost/regex.hpp"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


using namespace std;
//using namespace boost;

class A
{

public:
A(){aa = 1; bb =2; pp = NULL;}
//int operator == (const A& obj1){return true;}
friend bool operator == (const A& obj1, const A& obj2);

public:
int aa;
int bb;
int* pp;
};


bool operator == (const A& obj1, const A& obj2)
{
return true;
}

vector<A> a;
vector<A> b;

vector<CString> q;
vector<CString> s;



int _tmain(int argc, _TCHAR* argv[])
{

a.push_back(5);
a.push_back(6);
b = a;
if(b == a) //注意这句,如果==不是友元函数, 这句会编译失败, 我还没想通是为什么
{
//...此处省略N字(非黄色段子)
}

return 0;

}
i_liangwei 2011-01-11
  • 打赏
  • 举报
回复
非常感谢你的回答,我个人其实也是这么想的,不过不敢确定,“害怕”微软在此有后招,O(∩_∩)O~!

[Quote=引用 6 楼 arong1234 的回复:]
个人觉得这里应该直接用成员函数(我不觉得友元更灵活,不知道所谓的灵活性来源于哪里)不要以为微软的就一定有非常充足的原因,这里也许只是程序员临时犯傻
[/Quote]
arong1234 2011-01-09
  • 打赏
  • 举报
回复
个人觉得这里应该直接用成员函数(我不觉得友元更灵活,不知道所谓的灵活性来源于哪里)不要以为微软的就一定有非常充足的原因,这里也许只是程序员临时犯傻
hztj2005 2011-01-09
  • 打赏
  • 举报
回复
从编译的角度来看,运算符是一类名字特殊的函数,所以可以重载,既可以作为成员函数,也可以作为友元函数。
《C++编程思想》第12章用成员函数、友元函数两种方式给出了所有可重载的运算符的代码。

作为友元函数重载可能使用时更灵活一些,但大型程序或者商业上多方编程时有产生函数名重复的可能,所以就发展出成员函数来。
i_liangwei 2011-01-09
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 hztj2005 的回复:]
上述三个操作符为什么需要设定友元,直接把这三个操作符作为成员函数,应该也是可以的,你可以看看《C++编程思想》第12章。

实现代码中好像没用到CArchive类的私有属性或方法,WriteObject(pOb)可能是CArchive或其基类的方法了。
[/Quote]
首先非常感谢您的回答,但是我还不太明白您的意思,能不能稍微再详细点(PS:手头上暂时没有《C++编程思想》这一本书),谢谢!
就我现在的测试结果而言,貌似把上述MFC源码中所示的代码按照我的想法替换成如下代码(去掉friend,使之成为成员函数),编译运行之后,我的测试程序是没问题的(程序用到了对象序列化,并且debug发现的确运行了我改动的代码),具体改动如下!
第一段声明代码改动如下:

public:
// Object I/O is pointer based to avoid added construction overhead.
// Use the Serialize member function directly for embedded objects.
CArchive& operator<<(const CObject* pOb);

CArchive& operator>>(CObject*& pOb);
CArchive& operator>>(const CObject*& pOb);

// insertion operations
CArchive& operator<<(BYTE by);
CArchive& operator<<(WORD w);

第二段实现代码改动如下:

_AFX_INLINE CArchive& CArchive::operator<<(const CObject* pOb)
{ WriteObject(pOb); return *this; }
_AFX_INLINE CArchive& CArchive::operator>>(CObject*& pOb)
{ pOb = ReadObject(NULL); return *this; }
_AFX_INLINE CArchive& CArchive::operator>>(const CObject*& pOb)
{ pOb = ReadObject(NULL); return *this; }
  • 打赏
  • 举报
回复
是不是涉及到变量的显示转换和隐式转换的问题
hztj2005 2011-01-09
  • 打赏
  • 举报
回复
上述三个操作符为什么需要设定友元,直接把这三个操作符作为成员函数,应该也是可以的,你可以看看《C++编程思想》第12章。

实现代码中好像没用到CArchive类的私有属性或方法,WriteObject(pOb)可能是CArchive或其基类的方法了。
极简Qt 2011-01-09
  • 打赏
  • 举报
回复
没有研究过MFC的源码,向楼主学习!等待大牛讲解。
个人猜想,是微软有他的考虑吧,友元在实际项目中,我还没用到。。。
VC++ MFC 经典教程 - 基础篇 1.CP_YourFirstWindowsProgram.mp4 10.MFC_GDI_画直线和曲线.mp4 11.MFC_GDI_画椭圆_多边形及其他形状.mp4 12.MFC_GDI_画笔和画刷.mp4 13.MFC_GDI_画文本和字体.mp4 14.MFC_GDI_备用对象和取消选定.mp4 15.MFC_Ruler.mp4 16.MFC_窗口滚动条.mp4 17.MFC_Accel.mp4 18.MFC_Accel(2).mp4 19.MFC_消息框.mp4 2.Windows_编程模型.mp4 20MFC_客户区鼠标消息.mp4 21.MFC_TicTac-1.mp4 22.MFC_TicTac-2.mp4 23.MFC_TicTac-3.mp4 24.MFC_鼠标滚轮.mp4 25.MFC_捕获鼠标.mp4 26.MFC_VisualKB-1.mp4 27.MFC_VisualKB-2.mp4 29.MFC_菜单.mp4 3.MFC_第一个MFC程序设计.mp4 30.MFC_CButton类.mp4 31.MFC_E_FontView-1.mp4 32.MFC_E_FontView-2.mp4 33.MFC_CEdit类.mp4 34.MFC_MyPad.mp4 35.MFC_对话框_静态文本_编辑框.mp4 36.MFC_对话框_访问控件_7种方法_A.mp4 37.MFC_对话框_访问控件_7种方法_B.mp4 38.MFC_对话框_访问控件_7种方法_C.mp4 39.MFC_对话框_复选框_单选钮.mp4 4.MFC_消息映射.mp4 40.MFC_模态对话框.mp4 41.MFC_非模态对话框.mp4 42.MFC_属性表.mp4 43.MFC_公用对话框.mp4 44.MFC_数组类-1.mp4 45.MFC_数组类-2.mp4 46.MFC_CArray.mp4 47.MFC_列表类.mp4 48.MFC_映射类.mp4 49.MFC_类型指针类.mp4 5.MFC_使用向导快速进行MFC程序设计.mp4 50.MFC_CFile.mp4 51.MFC_CArchive.mp4 52.MFC_四个对象四种方法.mp4 53. MFC_Ruler.mp4 54.MFC_Ruler.mp4 55.MFC_Ruler.mp4 56.MFC_SdiSquares.mp4 57.MFC_Scroll_Ruler.mp4 58.MFC_CHtmlView.mp4 59.MFC_CTreeView.mp4 6.MFC_字符集和TEXT宏.mp4 60.MFC_CListView.mp4 61.MFC_MdiSquares.mp4 62.MFC_动态拆分窗口.mp4 63.MFC_ToolBar.mp4 64.MFC_ToolBar_Ex13a.mp4 65.MFC_StatusBar.mp4 66.MFC_StatusBar_Ex13b.mp4 67.MFC_Rebar.mp4 68.MFC_EZPrint.mp4 69.MFC_Print_Bubble.mp4 7.MFC_建立应用程序.mp4 8.MFC_第一个MFC程序设计(不用向导).mp4 9.MFC_Windows_GDI_设备描述表类.mp4 数据结构与算法_C语言 01.swap.mp4 02.BubbleSort.mp4 03.SelecttionSort.mp4 04.顺序查找.mp4 05.C_DS_折半查找.mp4 06.递归.mp4 07递归算法_折半查找.mp4 08.Permutations.mp4 09.插入排序.mp4 10.快速排序.mp4 11.归并排序.mp4 12.顺序栈.mp4 13.顺序队列.mp4 14.链表的基本概念.mp4 15.单链表的基本运算.mp4 16.循环单链表.mp4 17.双向链表.mp4 18.链式栈.mp4 19.链式队列.mp4 20.基数排序.mp4 21.树.mp4 22.二叉树的存储表示与实现.mp4 23.二叉树的遍历.mp4 24.二叉查找树.mp4 25.红黑树.mp4 26.堆.mp4 27.堆排序.mp4 28.哈希表.mp4 29.图_邻接矩阵.mp4 30.邻接表.mp4 31.图_深度优先搜索.mp4 32.图_广度优先搜索.mp4 快速通过_计算机二级_C语言 dk2j_c_calloc.mp4 dk2j_c_fopen_fclose.mp4 dk2j_c_goto语句.mp4 dk2j_c_if语句.mp4 dk2j_c_main参数和文件复制.mp4 dk2j_c_malloc.mp4 dk2j_c_realloc.mp4 dk2j_c_全局变量.mp4 dk2j_c_关系运算和逻辑运算.mp4 dk2j_c_函数的存储分类.mp4 dk2j_c_变量.mp4 dk2j_c_字符IO.mp4 dk2j_c_字符串函数.mp4 dk2j_c_字符串数组.mp4 dk2j_c_字符串的输入和输出.mp4 dk2j_c_字符型数据.mp4 dk2j_c_实型数据.mp4 dk2j_c_局部变量.mp4 dk2j_c_常量.mp4 dk2j_c_库函数.mp4 dk2j_c_数据输入.mp4 dk2j_c_数据输出.mp4 dk2j_c_整型数据.mp4 dk2j_c_标识符.mp4 dk2j_c_程序设计的基本概念.mp4 dk2j_c_算术运算符.mp4 dk2j_c_自加自减逗号运算符.mp4 dk2j_c_赋值表达式.mp4 dk2j_c_赋值语句_复合语句_空语句.mp4 KRC0507_多维数组.mp4 lc_流定位.mp4 MCU51_位操作运算符.mp4 MCU51_分支控制.mp4 MCU51_循环控制.mp4 MCU51_编译预处理.mp4 MCU_51_一维数组.mp4 MCU_51_二维数组.mp4 MCU_51_函数概述.mp4 MCU_51_字符数组.mp4 MCU_51_局部变量和全局变量.mp4 MCU_51_指针_数组与字符串指针.mp4 MCU_51_指针与函数参数.mp4 MCU_51_指针变量和指针运算符.mp4 MCU_51_指针数组.mp4 MCU_51_结构.mp4 MCU_51_结构数组_结构与函数.mp4 MCU_51_联合.mp4 PonC_指针和数组-2.mp4 VSE_6_安装.mp4 VS_2008_速成版_下载和安装.mp4 大家可以学的C语言 ABG2C_Cpp_更强大的C.mp4 ABG2C_for_循环.mp4 ABG2C_两个预处理器指令.mp4 ABG2C_从何处入手.mp4 ABG2C_关系运算符.mp4 ABG2C_初识C语言.mp4 ABG2C_变量.mp4 ABG2C_字符串.mp4 ABG2C_字符和字符串函数.mp4 ABG2C_循环.mp4 ABG2C_数学函数.mp4 ABG2C_数学运算.mp4 ABG2C_更高级的运算符.mp4 ABG2C_注释.mp4 ABG2C_测试多个值.mp4 ABG2C_终止循环.mp4 ABG2C_表达式还能用来做什么.mp4 ABG2C_输入.mp4 ABG2C_输入和输出.mp4 ABG2C_输出.mp4 ABG2C_逻辑运算符.mp4 CppDS_BubbleSort.mp4 CppDS_折半查找.mp4 CppDS_顺序查找.mp4 C_DR_函数.mp4 dk2j_c_fopen_fclose.mp4 dk2j_c_malloc.mp4 dk2j_c_字符IO.mp4 MCU_51_一维数组.mp4 MCU_51_结构.mp4 PonC_指针和函数.mp4 PonC_指针和数组-1.mp4 PonC_指针和数组-2.mp4 VC++ MFC快速入门 001.MFC_应用程序类型.mp4 002.MFC_对话框_静态文本_编辑框.mp4 003.MFC_对话框_访问控件_7种方法_A.mp4 004.MFC_对话框_访问控件_7种方法_B.mp4 005.MFC_对话框_访问控件_7种方法_C.mp4 006.MFC_对话框_复选框_单选钮.mp4 007.MFC_ComboBox_ListBox.mp4 008.MFC_ScrollBar.mp4 009.MFC_Spin.mp4 010.MFC_Progress.mp4 011.MFC_Slider.mp4 012.MFC_ListControl.mp4 013.MFC_TreeControl.mp4 014.MFC_DateTimePicker.mp4 015.MFC_Menu.mp4 016.MFC_Toolbar16.mp4 017.MFC_Toolbar24.mp4 018.MFC_StatusBar.mp4 019.MFC_两种对话框.mp4 020.MFC_文件对话框.mp4 021.MFC_字体和颜色对话框.mp4 022.MFC_图片控件_动画控件.mp4 023.MFC_属性页控件.mp4 024.讲MFC_窗口指针.mp4 025.MFC_窗口操作.mp4 026.MFC_发送消息.mp4 027.MFC_映射消息.mp4 028.MFC_自定义消息.mp4 029.MFC_拦截消息.mp4 030.MFC_定时器.mp4 031.讲VDD_注册表.mp4 032.MFC_系统度量.mp4 033.MFC_注销_关机_重启.mp4 034.MFC_鼠标消息.mp4 035.MFC_拖动无边框窗体.mp4 036.MFC_模拟鼠标键盘消息.mp4 037..创建进程.mp4 038.打开进程_终止进程.mp4 039..TH_管道.mp4 040..TH_双管道.mp4 041.MFC_只运行一个实例.mp4 042.内存映射文件.mp4 043.内存映射_共享数据.mp4 044.枚举进程-1.mp4 045.枚举进程-2.mp4 046.枚举进程-3.mp4 047.枚举进程-4.mp4 048.MFC_进程和线程.mp4 049.MFC_创建线程.mp4 050.MFC_线程控制.mp4 051.MFC_线程同步.mp4 052.MFC_线程同步方法.mp4 053.MFC_缩放位图.mp4 054.MFC_画刷.mp4 055.MFC_GDIPlus.mp4 056.MFC_使用GDIPlus打开和保存图片.mp4 057.MFC_CFile.mp4 058.MFC_CFile_家族.mp4 059.MFC_DBAPI_简介.mp4 060.MFC_初始化_ADO.mp4 061._数据库连接.mp4 062.MFC_ADO_MSSQL_Select.mp4 063.MFC_MiniMS_1.mp4 064.MFC_MiniMS_2.mp4 065.MFC_MiniMS_3.mp4 066.WS_Socket_编程原理.mp4 067.WS_TCP_Socket.mp4 068.WS_TCP_Socket_Client.mp4 069.WS_UDP_Socket_Receiver.mp4 070.WS_UDP_Socket_Sender.mp4 071.MFC_抓取网页.mp4 072.MFC_HOOK.mp4 073.MFC_全局键盘钩子.mp4 074.MFC_PlaySound.mp4 075.MFC_MCI.mp4 076.MFC_MCI_MP3_Player_1.mp4 077.MFC_MCI_MP3_Player_2.mp4 078.MFC_框架结构剖析.mp4 079.MFC_第一个MFC程序设计.mp4 080.MFC_消息映射.mp4 081.MFC_使用向导快速进行MFC程序设计.mp4 082.MFC_建立应用程序.mp4 083.MFC_第一个MFC程序设计(不用向导).mp4 084.在窗口中显示按钮.mp4 085.MFC_InnoSetup.mp4 C语言高级教程 - 指针和结构体 01.PonC_指针-1.mp4 02.PonC_指针-2.mp4 03.PonC_指针实例.mp4 04.PonC_指针运算.mp4 05.PonC_指针和函数.mp4 06.dk2j_cpp_函数指针.mp4 07.PonC_转换表.mp4 08.PonC_指针和数组-1.mp4 09.PonC_指针和数组-2.mp4 10.PonC_字符串函数-1.mp4 11.PonC_字符串函数-2.mp4 12.PonC_字符串函数-3.mp4 13.C语言字符串的缺点.mp4 14.MCU_51_结构.mp4 15.MCU_51_结构数组_结构与函数.mp4 16.API_数据结构.mp4 17.时间信息.mp4 18.文件属性和时间.mp4 19.遍历目录.mp4 20.递归目录.mp4 21.系统信息.mp4 22.选择字体.mp4 23.LOGFONT.mp4 C++ 编程思想 第1卷 1.对象导言.mp4 10.友元.mp4 11.句柄类.mp4 12.初始化与清除_1.mp4 13.初始化与清除_2.mp4 14.函数重载.mp4 15.联合.mp4 16.默认参数.mp4 17.默认参数还是函数重载.mp4 18.常量_1.mp4 19.常量_2.mp4 2.FirstCpp.mp4 20.常量_3.mp4 21.常量_4.mp4 22.常量_5.mp4 23.常量_6.mp4 24.内联函数.mp4 25.带内联函数的Stash和Stack.mp4 26.改进的错误检查.mp4 27.访问函数.mp4 28.函数内部的静态变量.mp4 29.静态对象.mp4 3.SecondCpp.mp4 30.控制连接.mp4 31.名字空间_1.mp4 32.名字空间_2.mp4 33.静态成员_1.mp4 34.静态成员_2.mp4 35.单例模式.mp4 36.替代连接.mp4 37.引用.mp4 38.拷贝构造函数.mp4 39.运算符重载_1.mp4 4.函数要点和函数库.mp4 40.运算符重载_2.mp4 41.运算符重载_3.mp4 42.运算符重载_5.mp4 43.重载赋值操作符_1.mp4 44.重载赋值操作符_2.mp4 45.重载赋值操作符_3.mp4 46.自动类型转换.mp4 47.动态创建对象.mp4 48.delete_可能会出错.mp4 49.指针的Stash.mp4 5.CStash.mp4 50.继承语法.mp4 51.继承和组合.mp4 52.继承中的构造和析构.mp4 53.继承的其它要点.mp4 54.继承中的向上类型转换.mp4 55.再论继承和组合.mp4 56.多态性和虚函数_1.mp4 57.多态性和虚函数_2.mp4 58.多态性和虚函数_3.mp4 59.多态性和虚函数_4.mp4 6.CStashLib.mp4 60.多态性和虚函数_5.mp4 61.多态性和虚函数_6.mp4 62.模板语法.mp4 63.模板中的常量.mp4 64.简单的Stack模板.mp4 65.链表的Stack模板.mp4 66.打开和关闭所有权.mp4 67.模板化的指针Stash.mp4 68.迭代器简介_1.mp4 69.迭代器简介_2.mp4 7.Stash.mp4 70.迭代器简介_3.mp4 71.函数模板_1.mp4 8.Stack.mp4 9.访问控制.mp4

16,551

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧