1

我有以下代码,对于我的生活,我无法理解为什么会出现访问冲突异常?我什至删除了所有的 OBJ、TDS 等文件并将其放入一个新项目中,仍然发生访问冲突。

本质上,此代码在 TFrame 中显示 TListView,并显示世界各地不同时区的各种当前时间。

注意:代码在 C++ Builder 6 中。

有人可以帮忙吗?

血腥地狱更新:已解决。我不应该在 TFrame 构造函数中向 TListView 添加项目。哑巴哑巴。

重大更新:似乎当通过计时器调用 UpdateTimes() 时,“li->Deleting”属性为 TRUE。当在定时器外调用时,它是 FALSE。现在为什么将“li->Deleting”设置为“true”,因为它是从计时器调用的?如果我做:

if(li->Deleting == false)
{
  li->Caption = "abcd";
}

它没有进入 if(),当从计时器调用 UpdateTimes() 时...... arggggh !!!

更新:似乎如果我在 TTimer 之外调用 UpdateTimes(),它工作正常。但是当从计时器调用时,它会引发访问冲突。是什么赋予了?

头文件:

#ifndef CurrentTimes_FrameH
#define CurrentTimes_FrameH
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
#include <ComCtrls.hpp>
#include <list>
using namespace std;
//---------------------------------------------------------------------------
struct LOCATIONTIMEINFORMATION
{
  AnsiString TimeZoneName;
  AnsiString PlaceName;
  int    UtcOffsetMinutes;
  TListItem* ListItem;
};
//---------------------------------------------------------------------------
class TCurrentTimesFrame : public TFrame
{
__published:    // IDE-managed Components
    TTimer *Timer;
    TListView *ListView;
    void __fastcall TimerTimer(TObject *Sender);
private:    // User declarations
public:     // User declarations
    __fastcall TCurrentTimesFrame(TComponent* Owner);
//---------------------------------------------------------------------------
//User Code
//---------------------------------------------------------------------------
private:
    list<LOCATIONTIMEINFORMATION>   FTimeInformation;
  typedef list<LOCATIONTIMEINFORMATION>::iterator LocationTimeInformationItr;
public:
  void AddTimeInformation(LOCATIONTIMEINFORMATION lti);
  void UpdateTimes();
};
//---------------------------------------------------------------------------
#endif

CPP 文件:

#include <vcl.h>
#pragma hdrstop
#include "CurrentTimes_Frame.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
//---------------------------------------------------------------------------
__fastcall TCurrentTimesFrame::TCurrentTimesFrame(TComponent* Owner): TFrame(Owner)
{
  Timer->Enabled = false;
  <strike>{
    LOCATIONTIMEINFORMATION lti;
    lti.TimeZoneName = "UTC";
    lti.PlaceName = "Near Greenwich, England";
    lti.UtcOffsetMinutes = 0;
    AddTimeInformation(lti);
  }</strike>
  //UPADTED: Don't add TListItem from constructor 
}
//---------------------------------------------------------------------------
void TCurrentTimesFrame::AddTimeInformation(LOCATIONTIMEINFORMATION lti)
{
  TListItem* li = ListView->Items->Add();
  li->Caption = lti.TimeZoneName;
  li->SubItems->Add(lti.PlaceName);
  li->SubItems->Add(lti.UtcOffsetMinutes);
  li->SubItems->Add("<time will come here>");
  lti.ListItem = li;
  ShowMessage(AnsiString(lti.ListItem->ClassName())); //Correctly shows "TListItem"
  FTimeInformation.push_back(lti);

  {
  LOCATIONTIMEINFORMATION temp = FTimeInformation.front();
  ShowMessage(AnsiString(temp.ListItem->ClassName())); //Correctly shows "TListItem"
  }
  Timer->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TCurrentTimesFrame::TimerTimer(TObject *Sender)
{
    UpdateTimes();
}
//---------------------------------------------------------------------------
void TCurrentTimesFrame::UpdateTimes()
{
  Timer->Enabled = false;
  TListItem* li;
  for(LocationTimeInformationItr itr=FTimeInformation.begin();itr!=FTimeInformation.end();itr++)
  {
    li = itr->ListItem;

    ShowMessage(AnsiString(li->ClassName())); //Access Violation:
    /*
    ShowMessage() above shows:

    ---------------------------
    Debugger Exception Notification
    ---------------------------
    Project XX.exe raised exception class EAccessViolation with message 'Access violation at address 4000567D in module 'rtl60.bpl'. Read of address 00000000'. Process stopped. Use Step or Run to continue.
    ---------------------------
    OK   Help
    ---------------------------
    */
  }
  Timer->Enabled = true;
}
//---------------------------------------------------------------------------

更新演示该列表的示例代码将项目作为副本,而不是参考。(据我所知,如果我在下面的代码中犯了一些错误,请纠正我)

@克雷格杨:

我很困惑...我认为结构会作为副本而不是参考添加到列表中?请看下面的代码,好像是在复制?还是我错过了一些基本的东西?还是下面的编码错误?

void PopulateData()
{
    AnsiString DebugText;
    list<LOCATIONTIMEINFORMATION> Data;

  LOCATIONTIMEINFORMATION OnStack;

  //Prints "junk"
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);

    OnStack.TimeZoneName = "UTC";
    OnStack.PlaceName = "Near Greenwich, England";
    OnStack.UtcOffsetMinutes = 10;
    OnStack.ListItem = (TListItem*)20;

  //OnStack:
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);
  //Add data to list
    Data.push_back(OnStack);

  //Get struct from list
  LOCATIONTIMEINFORMATION InList = Data.front();

  //OnStack:
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);
  //InList:
  DebugText.sprintf("%s,%s,%d,%d",InList.TimeZoneName,InList.PlaceName,InList.UtcOffsetMinutes,(int)InList.ListItem);

  //Change OnStack
    OnStack.TimeZoneName = "NONE";
    OnStack.PlaceName = "USA";
    OnStack.UtcOffsetMinutes = 50;
    OnStack.ListItem = (TListItem*)90;

  //OnStack:
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);
  //InList:
  DebugText.sprintf("%s,%s,%d,%d",InList.TimeZoneName,InList.PlaceName,InList.UtcOffsetMinutes,(int)InList.ListItem);

  //Change InList:
    InList.TimeZoneName = "SOME";
    InList.PlaceName = "BRAZIL";
    InList.UtcOffsetMinutes = 66;
    InList.ListItem = (TListItem*)88;

  //OnStack:
  DebugText.sprintf("%s,%s,%d,%d",OnStack.TimeZoneName,OnStack.PlaceName,OnStack.UtcOffsetMinutes,(int)OnStack.ListItem);
  //InList:
  DebugText.sprintf("%s,%s,%d,%d",InList.TimeZoneName,InList.PlaceName,InList.UtcOffsetMinutes,(int)InList.ListItem);
}
4

3 回答 3

3

编辑:我的回答不正确,我决定将其保留,因为值得牢记的是,如果您的集合(列表)通过引用保存元素,那么这是“奇怪的访问违规”的非常现实的可能性。如果不是 STL 列表保留了元素的副本,所描述的症状将完全相关。

嗨廖,

您写道:“ BLOODY-HELL-UPDATE:已解决。我不应该在 TFrame 构造函数中将项目添加到 TListView。”

我会不同意你的观点;你还没有解决它。虽然不一定是一个好主意(就设计而言),但在 TFrame 构造函数中向 TListView 添加项目不应导致访问冲突。

编辑:尽管我在下面的回答不正确,但我仍然不同意廖的“血腥地狱更新”。在 TFrame 构造函数中向 TListView 添加项不应导致访问冲突。事实上,我采用了原始代码并在 CPBB 2009 中对其进行了测试,并且运行良好。这表明错误可能在于框架的使用方式;或未演示的代码的其他方面。

问题在于构造函数中的以下行:

LOCATIONTIMEINFORMATION lti;
  • 这会在堆栈上分配 lti。
  • 然后将 lti 添加到列表中;或更准确地说:您将 lti 的引用添加到 list
  • 当您的构造函数超出范围时,lti 也会;并且该内存可以被应用程序的任何其他部分重用。
  • 稍后当您的计时器尝试更新时,FTimeInformation 中的引用仍然存在。
  • 您可以使用此引用来查找 lti 所在位置。
  • 如果该部分内存已被应用程序的任何其他部分更改,则ltr->ListItem不再引用在构造函数中创建的 TListItem。相反,它引用它尝试使用的内存的其他部分,就好像它是一个 TListItem。因此,您会遇到“奇怪”的问题,例如:
    • li->删除 == false
    • li->ClassName 导致访问冲突。

注意:您是否真的遇到访问冲突通常取决于运气:如果您确实遇到访问冲突,请认为自己很幸运;另一种选择通常是“莫名其妙”的不稳定行为。

尝试如下修改您的构造函数,它应该修复访问冲突。注意:lti 现在是动态分配的,你必须决定何时释放它,否则你会有内存泄漏;)

LOCATIONTIMEINFORMATION* lti = new LOCATIONTIMEINFORMATION;
lti->TimeZoneName = "UTC";
lti->PlaceName = "Near Greenwich, England";
lti->UtcOffsetMinutes = 0;
AddTimeInformation(*lti);
于 2009-12-05T21:29:36.870 回答
0

我实际上看不出代码有任何问题。

尝试从迭代器中打印 TimeZoneName 或 PlaceName,而不是 li->ClassName(),以确保您没有不小心将其他内容添加到列表或其他内容中......

于 2009-12-05T10:55:41.220 回答
0

您在 FTimeInformation 中有什么价值?例如,li == NULL 吗?


如果我们可以假设访问冲突在循环中第一次发生,并且 li 指向一个有效的 TListItem,那么也许我们应该将该行的三个语句分成三行。像这样的东西:

const char* className = li->ClassName();
AnsiString  ansiString(className);
ShowMessage(ansiString);

如果访问冲突没有发生在第一行,这将告诉我们一些有趣的事情。

于 2009-12-05T10:57:45.973 回答