我目前正在使用C ++ / CLI开发一个项目,而我正试图围绕内存分配。在我的代码中,我需要从外部库调用我无法编辑或读取源代码的方法。我遇到的具体问题如下。他们有类似这样的课程:
class Item {
public:
void SetName(const char* name);
};
没有什么太不寻常的,它有一个设置名称的方法......但是等一下......我怎么能释放那个char *指针?如果我从我的代码中调用它:
void MyMethod(Item* myItem){
char* name = CallOtherMethodThatGivesMeTheName();
myItem->SetName(name);
};
我什么时候应该取消分配姓名?我不能永远等待这个函数,直到myItem不再使用它,我完全不知道库是如何实现SetName()的。
我应该删除此范围内的名称吗?这不意味着myItem不再有一个有效的指针吗?也许我应该使用某种智能指针?一般来说,有没有一种标准的方法来处理这种情况?
由于我使用的是C ++ / CLI,我的实际方法如下所示,但我正在寻找一个通用解决方案(这可能也是错误的)。
void MyMethod(Item* myItem, String^ name){
pin_ptr<const wchar_t> pinned_name = PtrToStringChars(name)
myItem->SetName(pinned_name);
};
感谢您的帮助!
答案 0 :(得分:1)
忽略问题的CLI部分,我提供以下答案。我不认为使用CLI与您的问题的核心相关。你真正想知道的是当第三方库认为你安全释放一些你传递它的分配字符串时。
根据您提供的代码:
class Item {
public:
void SetName(const char* name);
};
并且假设没有文档,我会假设库提供了您提供的c风格字符串的内部副本,并且没有理由担心自己将其释放得比平常更多。我从上下文中做出这个假设。那将是正确的事情。并非所有第三方图书馆都做“适当的事情”,但你不能无所不能。
例如,我会假设:
#include <string>
int main()
{
Item item;
{
std::string name("Sam");
item.SetName(name.c_str());
}
// Carry on doing things with item...
return 0;
}
工作得很好,直到我检查一些其他的行为。如果我担心第三方库表现不佳,那么我会在循环中测试上面的内容并观察内存,或见证访问违规,但直到我有理由这样做。
最后: )阅读库的文档。 )询问他们的用户组或论坛。 )如果没有,请使用直觉和上下文。 )如果出现意外情况,请进行测试。
答案 1 :(得分:0)
当您更改using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.IO.Compression;
namespace RestoreTFSShelve
{
internal class Program
{
private static void Main(string[] args)
{
string shelvesetName = "";
string connString = "";
SqlConnection cn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(@"
SELECT c.[CreationDate], c.[Content], v.FullPath
FROM [dbo].[tbl_Content] c
INNER JOIN [dbo].tbl_FileMetadata f ON f.ResourceId = c.ResourceId
INNER JOIN [dbo].tbl_FileReference b ON f.ResourceId = b.ResourceId
INNER JOIN [dbo].[tbl_PendingChange] pc ON pc.FileId = b.FileId
INNER JOIN [dbo].[tbl_Workspace] w ON w.WorkspaceId = pc.WorkspaceId
INNER JOIN [dbo].[tbl_Version] v ON v.ItemId = pc.ItemId AND v.VersionTo = 2147483647
WHERE w.WorkspaceName = '@ShelvesetName'", cn);
cmd.Parameters.AddWithValue("@ShelvesetName", shelvesetName);
DataTable dt = new DataTable();
new SqlDataAdapter(cmd).Fill(dt);
foreach (DataRow row in dt.Rows)
{
string[] arrFilePath = row[2].ToString().Split('\\');
string fileName = arrFilePath[arrFilePath.Length - 2];
byte[] unzippedContent = Decompress((byte[])row[1]);
File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), fileName), unzippedContent);
}
}
private static byte[] Decompress(byte[] gzip)
{
using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
{
const int size = 4096;
byte[] buffer = new byte[size];
using (MemoryStream memory = new MemoryStream())
{
int count = 0;
do
{
count = stream.Read(buffer, 0, size);
if (count > 0)
{
memory.Write(buffer, 0, count);
}
}
while (count > 0);
return memory.ToArray();
}
}
}
}
}
之后的值时,您调用了name
并且该库使用了原始提供的值,您知道它已被复制。
否则它始终使用当前指针,并且由于您不知道编译器在程序结束时释放对象的顺序,您无法安全地解除分配。
答案 2 :(得分:-2)
通常你只能用“新”“删除”你创建的内容。
使用指针返回数据的库以两种方式工作:
1-要求您分配内存并将指针传递给它们(数据类型* p)
2-为你分配并给你指针(返回数据类型*)或(参数数据类型** p)或(参数数据类型*&amp; p)
首先,你知道何时释放你的记忆。
第二种方式,当你完成数据处理时,你需要从同一个库中调用另一个函数来为你释放内存,像这种模式这样的对函数很常见(打开关闭)(免费分配)...
当然可以有其他方式,所以总是阅读文档是最好的。
在C ++ / CLI中,您不会删除使用“^”符号定义的对象,垃圾收集器会处理它们。