请问:vc中是不是不支持friend呢?

mben 2003-09-11 06:48:03
编译时总是出现:internal compilation error.
...全文
38 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
howtotell 2003-09-11
  • 打赏
  • 举报
回复
你把两个类写到一个文件里试试看。
Oversense 2003-09-11
  • 打赏
  • 举报
回复
支持
Lymtics 2003-09-11
  • 打赏
  • 举报
回复
肯定支持~~~~~~看看你的代码?
huanyun 2003-09-11
  • 打赏
  • 举报
回复
绝对支持啊
xiaohyy 2003-09-11
  • 打赏
  • 举报
回复
支持的。。可能你哪个地方搞错了。。
tonybaobao 2003-09-11
  • 打赏
  • 举报
回复
支持的。你把源代码贴出来看看。
gjd111686 2003-09-11
  • 打赏
  • 举报
回复
肯定支持
你看你要申明的友元是否有头文件包含
实现有两个类 CVector 存放数据的自定义动态数组,采用一维动态数组存储矩阵数据 CMatrix 实现的矩阵类 使用的时候包含#include "Matrix.h"就行 CMatrix的接口函数都在"Matrix.h"里面 CVector的接口函数在"Vector.h"里,"Matrix.h"里包含了"Vector.h" 具体用法与测试用例Main.cpp里有3个测试用例,分别是针对构造函数属性计算与运算符重载的 内已包含测试工程xp\vc6.0\上亲测通过,并经过BoundsChecker测试没有内存泄漏。有兴趣的童鞋可以下作参考。 注意: 1、下标都是从0开始,数学课上矩阵下标都是从1开始,但是工作后习惯0开始,矩阵M的第一个元素是M(0,0) 2、类型定死为double,原来作业是模板类,由于vc6对模版支持不好,另矩阵计算double类比较理想、整型几乎只能作加减 提供了多种初始化方式,int[]、float[]、double[]均可构造初始化,或则先构造出CVector再由CVector初始化。 3、定义了一个最大允许误差#define permit_eof (1.0e-13),判断相等使用宏 #define EQUAL(a,b) ( ((b) - (a) < permit_eof)&&((a) - (b) < permit_eof) ? (TRUE) : (FALSE) ) 正常输出的时候绝对值小于permit_eof 的时候清零处理,想要指定精度输出请用PrintOut 鸣谢:CSDN上supermegaboy君的C/C++左值性精髓,读后略有所感,空闲时重构了下大学时的作业,着重区分了函数返回值的左右值 =================================================附录:接口函数========================================================================== 开放的接口: CVector //构造函数 CVector(); virtual ~CVector(); CVector(const size_t &nSize;); CVector(const CVector & vIn);//拷贝构造函数 CVector(const double* const pData,const size_t &nSize;); CVector(const float* const pData,const size_t &nSize;); CVector(const int* const pData,const size_t &nSize;); //公开的成员函数 double at(const size_t& uIndex)const;//作右值 BOOL push_back(const double& dbIn ); BOOL resize(const size_t& nSize); size_t size()const; //重载操作符 double& operator()(const UINT& uIndex);//重载()运算符,可作左值 //重载的运算符 double& operator()(const size_t& xr,const size_t& xc);//重载()运算符,可作左值 CVector& operator=(const CVector &);//重载=运算符 double operator*(const CVector & )const;//重载*运算符,两向量相乘 CVector operator*(const double α)const;//重载*运算符,向量乘以实数alpha CVector& operator*=(const double α);//重载*=算符,向量乘以实数alpha CVector operator+(const CVector & )const;//重载+运算符,向量加上向量 CVector& operator+=(const CVector & );//重载+=算符,向量加上向量 CVector operator-(const CVector & )const;//重载+运算符,向量加上向量 CVector& operator-=(const CVector & );//重载+=算符,向量加上向量 CVector operator+(const double α)const;//重载+运算符,向量加上实数alpna CVector& operator+
实现有两个类 CVector 存放数据的自定义动态数组,采用一维动态数组存储矩阵数据 CMatrix 实现的矩阵类 使用的时候包含#include "Matrix.h"就行 CMatrix的接口函数都在"Matrix.h"里面 CVector的接口函数在"Vector.h"里,"Matrix.h"里包含了"Vector.h" 具体用法与测试用例Main.cpp里有3个测试用例,分别是针对构造函数属性计算与运算符重载的 内已包含测试工程xp\vc6.0\上亲测通过,并经过BoundsChecker测试没有内存泄漏。有兴趣的童鞋可以下作参考。 注意: 1、下标都是从0开始,数学课上矩阵下标都是从1开始,但是工作后习惯0开始,矩阵M的第一个元素是M(0,0) 2、类型定死为double,原来作业是模板类,由于vc6对模版支持不好,另矩阵计算double类比较理想、整型几乎只能作加减 提供了多种初始化方式,int[]、float[]、double[]均可构造初始化,或则先构造出CVector再由CVector初始化。 3、定义了一个最大允许误差#define permit_eof (1.0e-13),判断相等使用宏 #define EQUAL(a,b) ( ((b) - (a) < permit_eof)&&((a) - (b) < permit_eof) ? (TRUE) : (FALSE) ) 正常输出的时候绝对值小于permit_eof 的时候清零处理,想要指定精度输出请用PrintOut 鸣谢:CSDN上supermegaboy君的C/C++左值性精髓,读后略有所感,空闲时重构了下大学时的作业,着重区分了函数返回值的左右值 =================================================附录:接口函数========================================================================== 开放的接口: CVector //构造函数 CVector(); virtual ~CVector(); CVector(const size_t &nSize;); CVector(const CVector & vIn);//拷贝构造函数 CVector(const double* const pData,const size_t &nSize;); CVector(const float* const pData,const size_t &nSize;); CVector(const int* const pData,const size_t &nSize;); //公开的成员函数 double at(const size_t& uIndex)const;//作右值 BOOL push_back(const double& dbIn ); BOOL resize(const size_t& nSize); size_t size()const; //重载操作符 double& operator()(const UINT& uIndex);//重载()运算符,可作左值 //重载的运算符 double& operator()(const size_t& xr,const size_t& xc);//重载()运算符,可作左值 CVector& operator=(const CVector &);//重载=运算符 double operator*(const CVector & )const;//重载*运算符,两向量相乘 CVector operator*(const double α)const;//重载*运算符,向量乘以实数alpha CVector& operator*=(const double α);//重载*=算符,向量乘以实数alpha CVector operator+(const CVector & )const;//重载+运算符,向量加上向量 CVector& operator+=(const CVector & );//重载+=算符,向量加上向量 CVector operator-(const CVector & )const;//重载+运算符,向量加上向量 CVector& operator-=(const CVector & );//重载+=算符,向量加上向量 CVector operator+(const double α)const;//重载+运算符,向量加上实数alpna CVector& operator+=(const double α);//重载+=算符,向量加上实数alpha BOOL operator==(const CVector &)const;//重载==运算符 BOOL operator!=(const CVector &)const;//重载!=运算符 CMatrix //构造函数 CMatrix(); virtual ~CMatrix(); CMatrix(const CMatrix&);//拷贝构造函数 CMatrix(const size_t& n);//产生n阶单位阵 CMatrix(const size_t& nrow, const size_t& ncol);// CMatrix(const size_t& nrow, const size_t& ncol,const CVector& xdata);//产生nrow行,ncol列矩阵数据由xdata初始化 CMatrix(const size_t& nrow, const size_t& ncol,const double*const pData); CMatrix(const size_t& nrow, const size_t& ncol,const float* const pData); CMatrix(const size_t& nrow, const size_t& ncol,const int* const pData); //公开的成员函数 double At(const size_t& xr,const size_t& xc) const;//这个只能作为右值 CMatrix Trans()const;//A.T()返回矩阵A的转置副本 CVector diag()const;//矩阵上三角化后的对角向量//以此求矩阵的秩,矩阵的行列式等 double det()const;//求矩阵行列式 size_t rank()const;//矩阵的秩 CMatrix Inv()const;//求逆矩阵 inline BOOL IsNullMatrix()const{ return (BOOL)(m_nRowlen==0 || m_nCollen == 0);};//是否是空矩阵 BOOL IsSingularMatrix()const;//是否是奇异矩阵//即行列式为0 友函数 //科学计数法输出//想看较精确的数据的时候 friend void void PrintOut(const CMatrix& M,const size_t& nprecision = 6,std::ostream& os = std::cout); //产生的随机方阵,一般会是非异阵,供测试用 friend CMatrix randMatrix(const size_t &uSize;,int MAX);//随机产生n阶的方阵 //--------------------------重载部分-Overloaded Part---------------------------------- CMatrix& operator=(const CMatrix &);//重载=赋值运算符 double& operator()(size_t xr,size_t xc);//重载()运算符,A(i,j)即矩阵A的i行j列的元素, friend std::ostream & operator<<(std::ostream & ,const CMatrix &);//重载<<,可用cout输出矩阵 friend std::ostream & operator<<(std::ostream & ,const CVector&);//重载<<,可用cout输出向量 friend std::istream & operator>>(std::istream & CMatrix &);//重载>>,可用cin输入矩阵 CMatrix operator*(const double α)const;//重载*运算符,矩阵乘以实数alpha CVector operator*(const CVector &)const;//重载*运算符,矩阵乘以向量 CMatrix operator*(const CMatrix &)const;//重载*运算符,矩阵相乘 CMatrix& operator*=(const CMatrix &);//重载*=运算符 CMatrix operator^(const int α)const;//重载^幂运算符,A^alpha,alpha可以为负整数 CMatrix operator+(const CMatrix &)const;//重载+运算符,矩阵相加 CMatrix& operator+=(const CMatrix &);//重载+=运算符 CMatrix operator-(const CMatrix &)const;//重载- CMatrix& operator-=(const CMatrix &);//重载-=运算符 BOOL operator==(const CMatrix &)const;//重载==运算符,判断矩阵是否相等 BOOL operator!=(const CMatrix &)const;//重载!=运算符,判断矩阵是否不相等 CVector operator/(const CVector &)const;//重载/除运算符,向量左除矩阵,求Ax=b的x向量
Microsoft Agent技术应用
--AgentShell的实现原理介绍

[摘要]
本文介绍了如何应用Agent的以及AgentShell的实现原理和几个重要的技术处理。

[关键词]
Agent,COM,角色,语音识别,语音合成。

对Agent编程的方法主要有使用VB,VC等语言进行ActiveX调用,除此之外还有直接通过VC进行COM
编程调用。在VB调用Agent是最简单不过了,但由于VB程序本身存在诸多缺陷,很难在实际应用。
而在VC,由于Agent内部完全采用了UNICODE编码,同时还要处理各种繁杂的COM接口,从而也存在一
定的问题。AgentShell是建立在Agent和应用程序之间的一个外壳程序,通过它可将Agent复杂的COM接
口封装起来,转变为简单的函数调用,很好的实现对Agent的控制。同时AgentShell也作为一个独立的程
序,可处理英文自动朗读等功能,本文将详细介绍其实现原理。
(一) 原理介绍
AgentShell和Agent Server的连接是通过COM调用来实现的,对于与应用程序的通信是通过WM_COPYDATA
消息来实现的, 下图表示出AgentShell与其它程序的关系:
[ Agent Server ]
¦
[ COM调用 ]
¦
[ AgentShell ]
¦
[ 消息 ]
¦
[ 应用程序 ]

将一个Agent控制加载相应的动画和语音码我们称之为“角色”,一般使用COM调用创建一个Agent角色,
要经过以下几个过程:
[ 初始化COM ]
¦
[ 连接Agent COM Sever,创建Agent控制 ]
¦
[ 注册Agent控制的消息反应器(Notify Sink) ]
¦
[ 加载角色数据文件,创建一个角色(Character) ]
¦
[ 设置角色的语言、初始位置以及其它属性 ]
¦
[ 显示角色 ]

AgentShell定义以下全局变量来控制角色的属性和动作:
角色的消息ID: long g_lNotifySinkID。
角色ID: long g_lMyAgentID。
Agent控制指针: IAgentEx *g_pAgentEx。
角色指针: IAgentCharacterEx *g_pMyAgent。
角色消息反应器指针: AgentNotifySink *g_pSink。

使用以上变量可很容易的调用Agent的功能,如显示角色:
BOOL agentShow()
{
HRESULT hRes;
long lRequestID;

if( !g_pMyAgent)
return FALSE;
hRes = g_pMyAgent->Show(FALSE, &lRequestID);
if (FAILED(hRes))
return FALSE;
return TRUE;
}

(二) 角色的语言处理
目前Agent支持很多种语言,不仅是显示,还有语音合成和语音识辨(对于文,目前仅支持显示)。
语言又分为主语言和子语言(或为副语言),如文的主语言为文(LANG_CHINESE),子语言则可为
简体(SUBLANG_CHINESE_SIMPLIFIED)和繁体等。AgentShell定义两个全局变量表达角色的语种:

主语言:DWORD g_nMainLang。
子语言:DWORD g_nSubLang。
这样程序内必须根据当前语言的不同来显示不同的信息,如程序退出时的问候信:
首先定义不同的语言信息,可以为宏定义或资源数据:
#define MES_GOODBYEL"Goodbye!"
#define MES_GOODBYE_CH L"再见!"
#define MES_GOODNIGHTL"Good night!"
#define MES_GOODNIGHT_CH L"祝您晚安!"
以下为实现退出提示代码:
void Goodbye()
{
if( g_bAgentOK)
{
SYSTEMTIME time;
agentStop();
agentShow();
agentPlay(L"Wave");
GetLocalTime(&time);
// 根据时间不同提示不同信息
if( g_nMainLang == LANG_ENGLISH)
{
// 提示英文信息
if( time.wHour < 19)
agentSpeak(MES_GOODBYE);
else
agentSpeak(MES_GOODNIGHT);
}
else
{
// 提示文信息
if( time.wHour < 19)
agentSpeak(MES_GOODBYE_CH);
else
agentSpeak(MES_GOODNIGHT_CH);
}
agentHide();
// 等待若干时间
Sleep(MAX_QUIT_TIME);
}
}
当然以上介绍的只是一种较为简单的方法,仅在于描述这种原理。

(三) 实现自动朗读英文
实现自动朗读实际上是响应剪贴板消息的过程,当复制选种的文本信息时,系统自动发送WM_DRAWCLIPBOARD
消息给所有剪贴板监视队列的窗口,相应的窗口只要读取当前剪贴板内的信息进行朗读即可,具体实现如下:

安装剪贴板监视:
void InstallClipSpy()
{
g_hNextWnd = SetClipboardViewer(g_hMainWnd);
}

主窗口的回调函数相应剪贴板消息:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// 剪贴板窗口队列发生变化
case WM_CHANGECBCHAIN:
hwndRemove = (HWND)wParam; // handle of window being removed
hwndNext = (HWND) lParam;
if( hwndRemove == g_hNextWnd)
{
g_hNextWnd = hwndNext;
}
if( g_hNextWnd)
{
SendMessage(hwndNext, WM_CHANGECBCHAIN, wParam, lParam);
}
// 剪贴数据发生变化
case WM_DRAWCLIPBOARD:
// 是否自动阅读
if( g_bEnableRead)
{
// 阅读剪贴板信息
ReadClipText();
}
if( g_hNextWnd)
{
SendMessage(g_hNextWnd,WM_DRAWCLIPBOARD,wParam, lParam);
}
获取剪贴板信息并且朗读:
void ReadClipText()
{
if( g_bAgentOK)
{
// 只有文本文件才朗读
if( IsClipboardFormatAvailable(CF_TEXT))
{
if (OpenClipboard(g_hMainWnd))
{
LPWSTR pwsz;
UINT cch;
HGLOBAL hglb;
LPSTR lpstr;

hglb = GetClipboardData(CF_TEXT);
lpstr = (LPSTR)GlobalLock(hglb);
cch = lstrlen(lpstr);
if( cch > 0)
{
pwsz = new WCHAR[cch + 1];
MultiByteToWideChar(CP_ACP, 0, lpstr, -1, pwsz, cch);
pwsz[cch] = '';
agentSaveState();
agentPlay(L"Read");
agentSpeak(pwsz);
agentPlay(L"ReadReturn");
agentRestoreState();
delete pwsz;
}
GlobalUnlock(hglb);
CloseClipboard();
}
}
}
}
最后还须在程序退出时将当前窗口句柄从剪贴板监视队列移走:
void RemoveClipSpy()
{
ChangeClipboardChain(g_hMainWnd, g_hNextWnd);
}

(四) 与外部程序的接口
应用程序和AgentShell之间传递数据主要通过WM_COPYDATA消息实现,由于传递的数据类型各
有不同,所以需要定义一个数据结构来描述:
struct AgentActionSTRUCT
{
WORD nAction;
DWORD nD1;
DWORD nD2;
WCHAR sData[MAX_DATA_LEN];
};
nAction用来表示Agent应该执行的操作,如显示、表演等。nD1,nD2,sData用来记录传递的数据。
传递消息必须获取AgentShell主窗口的句柄,实现如下:
HWND GetAgentMainWnd()
{
return FindWindow(AGENT_CLASS_NAME, NULL);
}
由于Agent采用了UNICODE, 必须将ANSI字符转化为UNICODE字符:
BOOL SendMesToAgent(WORD nAction, DWORD nD1, DWORD nD2, LPCSTR sData)
{
UINT nSize;
HWND hWnd = GetAgentMainWnd();
if( hWnd)
{
action.nAction = nAction;
action.nD1 = (DWORD)nD1;
action.nD2 = (DWORD)nD2;
// 将ANSI符转换为UNICODE的字符
nSize = MultiByteToWideChar(CP_ACP, 0, sData, lstrlen(sData) + 1,
action.sData, MAX_DATA_LEN);
action.sData[nSize] = '';
//
COPYDATASTRUCT cds;
cds.dwData = (DWORD)0;
cds.cbData = (DWORD)sizeof(action);
cds.lpData = (VOID *)&action;
// 通过WM_COPYDATA消息与AgentShell交换数据
SendMessage(hWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
return TRUE;
}
return FALSE;
}
目前AgentShell提供的函数主要有:
// 启动角色外壳程序(AgentShell) bRun是否执行
BOOL agentAPIRun(BOOL bRun = TRUE);
// 退出角色外壳程序(AgentShell)
BOOL agentAPIExit();
// 创建一个新角色(sPath角色数据文件路径, nLang主语言, nSubLang子语言)
BOOL agentAPICreate(LPCSTR sPath, UINT nLang, UINT nSubLang);
// 设置角色名字(sName角色名字)
BOOL agentAPISetName(LPCSTR sName);
// 将角色卸载
BOOL agentAPIUnload();
// 显示角色
BOOL agentAPIShow();
// 隐藏角色
BOOL agentAPIHide();
// 显示或隐藏角色
BOOL agentAPIShowORHide();
// 停止角色表演
BOOL agentAPIStop();
// 角色表演(sAction动作名称)
BOOL agentAPIPlay(LPCSTR sAction);
// 角色讲话(sText句子)
BOOL agentAPISpeak(LPCSTR sText);
// 角色鞠躬(x,y 指方向)
BOOL agentAPIGesAt(WORD x, WORD y);
// 移动角色到指定的位置(x,y移动的坐标)
BOOL agentAPIMoveTo(WORD x, WORD y);
// 保存当前角色显示状态
BOOL agentAPISaveState();
// 恢复角色的状态
BOOL agentAPIRestoreState();
// 允许自动阅读
BOOL agentAPIEnableAutoRead();
// 禁止自动阅读
BOOL agentAPIDisableAutoRead();
注意传递给AgentShell的数据长度不要超过1K(实际上一般不会大于1K)。
2) 使用接口
有了以上介绍的接口函数,对Agent的控制变的很简单,以下是一个简单的问候示例:
// 启动AgentShell
if( agentAPIRun(TRUE))
{
// 保存当前Agent的状态
agentAPISaveState();
// 开始表演
agentAPIPlay(_T("Greet"));
// 讲话
agentAPISpeak(_T("hello, my friend."));
// 表演结束
agentAPIPlay(_T("GreetReturn"));
// 恢复原来状态
agentAPIRestoreState();
// 退出
agentAPIExit();
}

AgentShell在笔者的免费软件"我的助手"得到很好的利用,当然目前其仅涉及了Agent的一小部分内容,还有如语音识辨等,未做处理,还有待一步改进。以上程序在Visual C++ 6.0编译通过,源代码可到助手之家(http://www.helperHome.com)下载

16,472

社区成员

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

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

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