BCB常见错误和运行时异常

 

写在前面:当你在百度、谷歌上都搜索不到解决方案时,证明你的思路是错误的,应改变解决思路

1、unresolve external ''
 这些错误是由于: (1)工程中没有包含相应的cpp文件;(2).h中有函数声明,.cpp中没有函数实现;(3)类的CPP文件中,成员函数没有在方法名前加类名;(4)类静态成员变量没有在cpp文件中初始化;(5)这种情况出现在命名空间中,即.h文件中声明函数,并把函数放入命名空间中,但是.cpp文件中没有把函数放入命名空间中。(6)如果程序中使用了其他的.lib文件,那么这些编译生成这些.lib文 件的Runtime Library和程序应该是相同的,即都是/MT(/MTd)或都是/MD(/MDd)。如果不同,极有可能会出现这种链接错误,参考:https://blog.csdn.net/socrates/article/details/3704929https://qimo601.iteye.com/blog/1550348,因为.lib和exe分别使用了两个不同类型运行时库的相同签名的函数(/MT和/MD)。

2、[C++ Error] QueryCommon.cpp(1273): E2094 'operator+' not implemented in type 'Property' for arguments of type 'const char *'

由const标识符引起的,可能在Propery前加了const,然后程序中对Propery对象使用了下标操作符


3、[C++ Warning] Unit1.cpp(232): W8037 Non-const function ebank::Row::operator [](const std::string &) called for const object

由于对const Row对象使用了下标运算符,而下标运算符有可能改变Row对象的值

4、[C++ Error] algorithm(30): E2093 'operator==' not implemented in type 'string' for arguments of the same type
这是由于没有加入相应的头文件,或者头文件在#include <algorithm>语句之后加入的模板函数的定义要放在头文件中,详见”C++ Primer 3rd Edition 中文版.pdf    10.5.“

5、raised exception class EOSError with message 'System Error. Code: 1400. 无效的窗口句柄。'.这是由于在子线程中操作了vcl组件,而vcl组件不是线程安全的,只能在主线程中操作。

6、运行时异常:xxx raised exception class EAccessViolation with message 'Access violation at address xxxx in module 'rtl140.bpl.' Read of adrress xxxx.
这种异常一般是由于写入或读取了不可访问的内存,比如数组越界读写、引用的指针为空等;还有一种情况是exe与dll通信的时候,对方释放了不是自己申请的内存(即exe释放了dll中申请的内存或者dll释放了exe申请的内存),究其原因是由于dll和exe各自有自己的一套内存管理机制造成的(使用的堆不同),dll申请的内存必须由相应的dll释放,反之亦然;而且这种错误非常难跟踪,出现AV的地方并不是根源所在。

7、发布程序(生产独立运行的exe)

要生成可独立运行的exe,方法如下:
1)c++ builder 2009中,打开Project>>Options
2)Diretories and Conditionals中选择Base
3)C++ Linker中奖Dynamic RTL设置为False(默认为true)
4)Packages中去掉Build with runtime packages前面的勾(默认勾选)

5)可以在命令行使用tdump xxx.exe > a.txt把可执行程序依赖的库信息输出到文本文件中

6) 动态库程序也要如上设置,否则如果动态库有依赖库的时候LoadLibrary就会失败,用GetLastError函数获取错误代码为126,意思是没有找到指定的模块(其实是没有找到要加载的动态库依赖的动态库)。

8、在多线程处理中,最好不要使用局部静态变量,因为当一个线程修改了此静态局部变量后,另一个线程如果再读取此静态局部变量的值可能不是它想要的,因为这个值是被另一个线程修改的。

9、在升级IDE的时候(比如从BCB6到BCB2010),重新编译工程时可能会碰到:

[ILINK32 Error] Error: Unresolved external 'wWinMain' referenced from D:\PROGRAM FILES\EMBARCADERO\RAD STUDIO\7.0\LIB\C0W32W.OBJ

这个时候在工程.cpp文件中加入#include <tchar.h>即可.

10、在使用std::list进行数据存储时,不要如下顺序操作list

        list->remove(*it);

        delete *it;

这样有可能会发生access violation异常,初步分析可能是因为remove之后,it中的内容就被释放了,it指向的内存是无法预料的。要如下顺序写:

        delete *it;

        list->remove(*it);

11、在子线程中使用com时需要在子线程中初始化com(CoInitializeEx),而且要和CoUninitialize成对出现(详见CoInitializeEx帮助文档)。而且com对象的创建和销毁都要放在子线程中(在CoInitializeEx和CoUninitialize之间创建和销毁),否则可能会出现ACCESS VIOLATION异常,而且这种异常没有规律可言,有时出现有时不出现,出现时BCB的IDE也捕获不了(而且在XP下经常出现,在win7下不经常出现)。特别是在使用智能指针的时候要特别注意,因为智能指针的销毁只有在出了智能指针的作用域才会进行,而这个时候通常已经执行了CoUninitialize,这样也会报ACCESS violation异常,所以这个时候要用大括号“{”把智能指针的作用域括起来,这样当智能指针出了这个大括号的作用域时就会销毁,例如:

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD dwCoInit =0;
    CoInitializeEx(NULL, dwCoInit);
    {
        Excel::_ApplicationPtr pExcel;
        pExcel.CreateInstance(_T("Excel.Application"));
        Excel::_WorkbookPtr pBook;
        pBook = pExcel->Workbooks->Open("c:\\test.xls", varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption, varOption);
        Excel::_WorksheetPtr pSheet = pBook->Sheets->Item[1];
       Excel::RangePtr pRange = pSheet->GetRange(_bstr_t(_T("A1")));
        _variant_t vItem = pRange->Value2;
        printf(_bstr_t(vItem.bstrVal));    
        pBook->Close(VARIANT_FALSE);
        pExcel->Quit();
    }
    CoUninitialize();
    return0;}
这里pExcel和pBook就是智能指针,用大括号括起来后放入CoInitializeEx(NULL, dwCoInit)与CoUninitialize();之间。

 12、new char(size) 与 new char[size],前者申请一个字节大小的堆内存空间,且此字节内容为size;后者申请一个大小为size个字节的连续堆内存空间,内容未知。很多人如果在代码中如果由于失误,申请数组时使用了前一个,那么在使用这个内存地址时大部分情况下可能不会出错,特别是在内存比较充足的情况下,因为第一个返回的地址开始的几个连续字节可能都没有被操作系统和其他应用程序使用,所以使用时没有出问题,这样就造成一种错觉,好像new char(size)也能申请size个字节大小的内存空间,其实不然。当内存比较紧俏或者系统从堆中频繁申请内存时,就可能出现access violation或者invalid pointer operation等等内存错误,且这种错误都是运行时异常且不易被跟踪。
13、STL中的容器按存储方式分为两类,一类是按以数组形式存储的容器(如:vector 、deque);另一类是以不连续的节点形式存储的容器(如:list、set、map)。在使用erase方法来删除元素时,需要注意一些问题。在使用 list、set 或 map遍历删除某些元素时可以这样使用:

正确使用方法1

std::list< int> List;   
std::list< int>::iterator itList;
for( itList = List.begin(); itList != List.end(); )
{         
	if( WillDelete( *itList) )       
	{        
		itList = List.erase( itList);    
	}    
	else        
		itList++;
}

正确使用方法2

 

std::list< int> List;
std::list< int>::iterator itList;
for( itList = List.begin(); itList != List.end(); )
{           
	if( WillDelete( *itList) )    
	{            
		List.erase( itList++);    
	}        
	else        
		itList++;
}

其中List.erase(itList++);相当于以下语句:

std::list<int>::iterator tmp = itList;

++itList;

List.erase(itList++);

即先将itList传给erase的参数,然后再进行++运算,最后执行erase函数。

下面是错误使用方法。

错误使用方法1

std::list< int> List;
std::list< int>::iterator itList;
for( itList = List.begin(); itList != List.end(); itList++)
{     
    if( WillDelete( *itList) )
    {       
       List.erase( itList);
    }  
}

上面执行过erase后,itList指向的内存已不可访问,再执行for语句的++操作时,会造成AV异常。

错误使用方法2

std::list< int> List;
std::list< int>::iterator itList;
for( itList = List.begin(); itList != List.end(); )
{          
    if( WillDelete( *itList) )
    {             
        itList = List.erase( ++itList);
    }     
    else    
        itList++;    
}
 在使用 vector、deque遍历删除元素时,也可以通过erase的返回值来获取下一个元素的位置:

正确使用方法

std::vector< int> Vec;
std::vector< int>::iterator itVec;
for( itVec = Vec.begin(); itVec != Vec.end(); )
{ 
    if( WillDelete( *itVec) )  
    {      
        itVec = Vec.erase( itVec); 
    }    
    else    
        itList++; 
}

注意:vector、deque 不能像上面的“正确使用方法2”的办法来遍历删除,即数组容器不能使用erase(itVec++)或erase(++itVec)删除元素,因为数组删除过中间某个元素后,会引起后面元素的前移,指向后面元素的迭代器就变得不可用,解引用指向后面元素的迭代器时会引起AV异常或者容器内部的断言异常(一般为断言异常)。

14、Application->Terminate();在有子线程运行时并不能立刻使程序退出,所以在需要立刻退出的时候需要使用下面的函数:
void __fastcall TerminateSelf()
{
    HANDLE hself = GetCurrentProcess();
       TerminateProcess(hself, 0);
}
或者先强制退出子线程,再用Application->Terminate();例如:
HANDLE hThd = ...
   TerminateThread(hThd ,1);//强制结束线程
Application->Terminate();

15. 在使用STL容器的迭代时,必须如下这样使用:
std::list<string>iterator it = list.begin();
即在BCB环境下std标识不能省略,即使你使用了:using namespace std;在vc环境下可以省略。 

16.[BCC32 Error] Main.cpp(4086): E2357 Reference initialized with 'string', needs lvalue of type 'string'
此编译错误是因为在BCB下引用必须用变量初始化,而不能用临时变量初始化,在VC下却可以,如下所示:
string LTrim(string& str)
{
    string::size_type pos = str.find_first_not_of(" \n\r\t");
    if (pos != string::npos)
    {
        str = str.substr(pos);
    }
    return str;
}
string RTrim(string& str)
{
    string::size_type pos = str.find_last_not_of(" \n\r\t");
    if (pos != string::npos)
    {
        str = str.substr(0,pos+1);
    }
    return str;
}
string trim(string& str)
{
    string tmp = RTrim(str);
    return LTrim(tmp);
//    return LTrim(RTrim(str));//在vc下可用
} 

 

17、在使用TDataSet的时候,DisableControls和EnableControls函数必须配对使用,不能只使用一个,否则可能会报运行时异常:Dataset not in edit or insert mode。而且就算没有报异常,软件运行也会出现奇怪的现象,比如Grid中该显示的没有显示等等奇怪行为,如下所示:

 

void __fastcall TDumpFrm::RefreshUpGrid()
{    
	DataSetUp->DisableControls();
    DataSetUp->EmptyDataSet();
    WIN32_FIND_DATA find;
    String sWildcardFile = m_dumpJLPath + "*.Fmt";
    HANDLE hSearch = ::FindFirstFile(sWildcardFile.c_str(), &find);
    if (hSearch == INVALID_HANDLE_VALUE)
    {   
		//不要忘记在return分支中调用EnableControls   
		//(即DisableControls一定要与EnableControls配套使用),
		//非常重要!
		DataSetUp->EnableControls();
		return;
    }
    int valueSize = DataSetUp->FieldCount - 1;
    do
    {
		String fileName = find.cFileName;
        if (find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
			continue;
		}
        TStringList* list = Split(fileName, '_');
        if (list->Count < 4)
        {
			delete list;
            continue;
		}
        String dateTime = list->Strings[0];
        String operid = list->Strings[1];
        String drvid = list->Strings[2];
        String trainNum = list->Strings[3];
        delete list;
        TVarRec row[] = { false, fileName, find.nFileSizeLow };
        DataSetUp->AppendRecord(row, valueSize);
        Application->ProcessMessages();
	}while (::FindNextFile(hSearch, &find));
    FindClose(hSearch);
    if (DataSetUp->RecordCount > 0)
    {
		DataSetUp->RecNo = 1;
	}
    DataSetUp->EnableControls();
}
18、E2034 Cannot convert 'unsigned long (__stdcall * (_closure )(void *))(void *)' to 'unsigned long (__stdcall *)(void *)'
 此编译错误是在创建线程的时候,线程函数使用的是类成员函数,但声明的时候没有加static标识符。
19、使用__int64类型时一定要注意,一定要使用显示强制转换,尽量不要默认类型强制转换,如下所示:
UINT32 value32 = ...;
__int64 value = (__int64)value32;//正确
__int64 value64 = value32;//错误,转换后的结果可能为负数
以下也是错误的用法:
UINT32 dataLen = 1863;
__int64 offset = -dataLen;
或者
__int64 offset = 0 - dataLen;
此时的offset值并不是-1863,而是一个很大的值,这个比较奇怪,转换为int都不会出错;若要转换,可以使用如下方法:
__int64 offset = 0;
offset -= dataLen;
此时的offset为-1863。 
20、在一个文件中(UpManager.h)定义枚举类型 enum RET{OK,NETERR,LOGICERR}; 
编译的时候会报如下错误:
[BCC32 Error] UpManager.h(12): E2184 Enum syntax error
[BCC32 Error] UpManager.h(12): E2040 Declaration terminated incorrectly
[BCC32 Error] UpManager.h(12): E2190 Unexpected }
错误指向的地方是枚举类型定义的地方,仅仅从错误信息你看不出哪儿有问题,好像枚举类型定义也没有出错,怎么就报错误呢?!仔细分析发现,在另外一个头文件名中有预定义:#define NETERR             WM_USER + 100 //网络错误
是由于预定义与枚举定义的成员冲突导致上述错误,但这种错误极难发现,只有根据经验判断!
附注:在枚举类型中定义的常量,属于定义枚举的作用域,而不属于这个枚举类型。即enum所定义的类型不具备名字空间限定能力(因为不属于类类型),其所定义的常量子具备和enum类型所在名字空间相同的可见性,由于自身没有名字限定能力,所以会出现名字冲突现象。
21、在使用消息机制给主界面发送消息时要注意,主界面销毁后不要再发消息,否则可能会报AV异常。这种情况主要发生在以下情形:主界面创建子线程,子线程中需要给主界面发送消息,当主界面退出销毁后,子线程还在运行,这个时候子线程再给主界面发送消息时,主界面的窗口句柄都是无效的,所以会发生AV异常。 

 

22、使用ShellExecute函数时应注意,当调用的exe所在文件夹与调用者不在同一个文件夹时,应传递exe所在的路径给参数lpDirectory,否则exe不会从它所在的目录下查找自己需要的资源文件,而是从宿主程序所在的目录下查找资源文件。
23、使用c++builder制作dll时,链接错误:[ILINK32 Error] Error: Unresolved external '__fastcall Strhlpr::UnicodeFree(System::UnicodeString&)' referenced from C:\PROGRAM FILES\CODEGEAR\RAD STUDIO\6.0\LIB\DEBUG\VCLE.LIB|ustring这是由于制作动态库时,没有选中VCL动态库,而代码中使用了VCL类库所致。解决方案:(1)去除代码中的VCL类,比如AnsiString、UnicodeString等;(2)使用VCL格式的动态库

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值