【问题标题】:How to determine whether an object has heap allocated members?如何判断一个对象是否有堆分配的成员?
【发布时间】:2019-06-30 10:20:06
【问题描述】:

有没有办法确定一个对象是否至少有一个成员在堆上?

我试图能够将像 std::stringstd::vectorstd::list (是的,主要是容器)之类的对象与所有其他类型区分开来(不幸的是,即使具有单个元素的容器也在我的“兴趣范围”之内)

我正在尝试执行以下操作:

struct foo
{
private:
    int * _ptr;

public:
    foo() : _ptr(new int) {};
    ~foo() { delete _ptr; };
};

struct bar
{
private:
    int val;
};

template <typename T>
void func(T val)
{
    if constexpr (std::is_class_v<T>)
    {
        std::cout << std::setw(20) << typeid(T).name() << " is a class type." << std::endl;
        if (/* determine if foo has any heap allocations */)
        {
            // Do something #1.
            std::cout << std::setw(20) << typeid(T).name() << " does allocate on heap." << std::endl;
        }
        else
        {
            // Do something #2.
            std::cout << std::setw(20) << typeid(T).name() << " does NOT allocate on heap." << std::endl;
        }
    }
    else
    {
        // Do something #3.
        std::cout << std::setw(20) << typeid(T).name() << " is NOT a class type." << std::endl;
    }
}

int main()
{
    func(foo()); // foo does allocate on heap
    cout << endl;

    func(bar()); // bar does NOT allocate on heap
    cout << endl;
};

foobar 只是示例,函数func() 必须执行与cout 控制台不同的功能。

【问题讨论】:

  • 如果指针是动态分配的或本地分配的,您通常无法区分它们。如果你需要这样做,那显然是 ypu 存在严重设计缺陷的迹象。
  • 这看起来像是一个 XY 问题,因为一旦创建了一个对象,动态分配的对象(您错误地称为“堆分配”)和任何其他对象之间的唯一区别就是它的生命周期结束 - 假设没有未定义的行为。没有办法检测任意对象是否是动态分配的,除非以某种方式显式跟踪。如果您提出的问题实际上是可能的,您如何解释您实际期望能够实现的目标?
  • @πάνταῥεῖ 我不想区分堆栈数组和堆数组。我试图区分“class type with an array as a member”和“class type without an array as a member”。数组是否在堆上的堆栈对我来说没有区别。我只想获取该数组中的数据。
  • @Peter 这是 XY 问题...我正在尝试编写一个哈希函数,它会知道用户定义的键类型是否在数组上存储任何数据(就像 std::string 一样) .我相当能够创建一个采用任何类型的通用散列函数,但是如果该类型有一个指向其中一些存储数据的数组(指针),问题就会变得非常棘手(指向动态数组的指针在运行时发生变化,相同的键在,不同的时间会产生不同的hash值)
  • @cukier9a7b5 - 如果您的哈希取决于指针的值 - 与这些指针指向的值不同 - 那么它可能没有多大用处。 std::string 的值不取决于存储数据的地址 - 它取决于每个字符的值。

标签: c++ class templates c++17 member


【解决方案1】:

这是 XY 问题...我正在尝试编写一个哈希函数 注意用户定义的键类型是否在数组中存储任何数据 (就像 std::string` 会)。我相当有能力创建一个通用的 接受任何类型的散列函数,但如果该类型有一个数组(一个 指针)指向它的一些存储数据,问题变得非常棘手 (指向动态数组的指针在运行时改变,相同的键在, 不同的时间会产生不同的hash值)

请注意,某些结构/类类型具有内部未初始化的填充,这可能会使您的哈希函数无法正常工作。

否则,std::is_trivially_copyable 将是一个好的开始。

【讨论】:

  • 我完全忘记了填充。谢谢你。现在我的“问题”似乎更加复杂......(我想出的第一个解决方案是围绕能够创建一个能够检索类成员类型的type_trait)。
【解决方案2】:

正如其他人在 cmets 中指出的那样,没有办法区分指针是否指向动态分配。另一方面,C++ 标准 &lt;type_traits&gt; 提供类型特征,如

等等。查看链接,看看它是否适合您的实际问题。


话虽如此,为什么不能用一个告诉你需要什么信息的成员函数来解决它?

例如:

struct foo
{
public:
    bool isHeapAllocated() const noexcept { return true; }
};

struct bar
{
public:
    bool isHeapAllocated() const noexcept { return false; }
};

然后简单地调用函数:

template <typename T> void func(T val)
{
    if constexpr (std::is_class_v<T>)
    {
        if (val.isHeapAllocated()) { 
            // Do something #1.
        }
        else
        {
            // Do something #2.
        }
    }
    else
    {
        // Do something #3.
    }
}

【讨论】:

  • 这绝对是最简单最简单的解决方案。我确实考虑过 - 我仍然不使用它的唯一原因是因为我有点犹豫是否要强制“用户”在课程中添加一些特殊的信息。仅出于哈希函数的目的。否则,我已经在使用这个解决方案了。附言我只是为自己写的,所以可以肯定的是,我会默认使用这个解决方案。
【解决方案3】:

虽然我很确定其他答案是正确的,当他们说您不应该试图确定一个对象是位于堆还是堆栈上时,我不得不不同意,这是“不可能”确定的。

以下示例使用 windows(每个操作系统的过程都不同!):

#include <Windows.h>
bool isAddressOnHeap(void* address) {
    bool returnval = 0;
    PROCESS_HEAP_ENTRY entry;
    HANDLE procHeap = GetProcessHeap();
    if (procHeap == NULL) {
        std::cerr << "Failed to retrieve Heap.\n";
        return -1;
    }

    if (HeapLock(procHeap) == FALSE) {
        std::cerr << "Failed to lock heap.\n";
        return -1;
    }

    entry.lpData = NULL;
    if (HeapWalk(procHeap, &entry) != 0) {
        if (entry.wFlags & PROCESS_HEAP_REGION) {
            std::cout << "Function succeeded. The heap begins at " << (void*)entry.Region.lpFirstBlock << " and ends at " << (void*)entry.Region.lpLastBlock << "\n";
            if (address >= (void*)entry.Region.lpFirstBlock && address <= (void*)entry.Region.lpLastBlock) 
                returnval = 1;
        }
    }

    if (HeapUnlock(procHeap) == FALSE) {
        std::cerr << "Failed to unlock heap.\n";
        return -1;
    }
    return returnval;
}

此函数将确定给定地址是否在堆上。这里是测试程序:

int main()
{
    int i = 1;
    std::cout << "Address of i on Stack: " << (void*)&i << "\n";


    int* j = new int;
    *j = 1;
    std::cout << "Address of j on Heap: " << (void*)j << "\n";

    int* k = (int*)malloc(sizeof(int));
    *k = 1;
    std::cout << "Address of k on Heap: " << (void*)k << "\n";


    std::cout << "is i on Heap?: " << isAddressOnHeap(&i) << "\n";
    std::cout << "is j on Heap?: " << isAddressOnHeap(j) << "\n";
    std::cout << "is k on Heap?: " << isAddressOnHeap(k) << "\n";

    delete j;
    free(k);
    return 0;
}

程序在我的机器上返回以下内容:

Address of i on Stack: 0000001062AFFE80
Address of j on Heap: 0000018B11C530B0
Address of k on Heap: 0000018B11C53070
Function succeeded. The heap begins at 0000018B11C40740 and ends at 0000018B11D3F000
is i on Heap?: 0
Function succeeded. The heap begins at 0000018B11C40740 and ends at 0000018B11D3F000
is j on Heap?: 1
Function succeeded. The heap begins at 0000018B11C40740 and ends at 0000018B11D3F000
is k on Heap?: 1

您可以在Microsoft's website 上阅读有关此代码如何工作的更多信息。

在 Linux 机器上似乎有 similar ways 来执行此操作。

但作为最后的警告:这个答案只是一个概念证明!

【讨论】:

    【解决方案4】:

    我试图区分“以数组为成员的类类型”和“不以数组为成员的类类型”。

    你需要查看代码,所以你需要将你的函数传递到源代码的路径,然后你需要解析它。

    【讨论】:

      【解决方案5】:

      唯一可靠的方法是添加所需的机制来处理每种类型,并根据需要添加单个对象。

      以下是任何理智类型的良好开端(尊重零/三/五规则):

      template <class T>
      constexpr auto owns_no_dynamic_memory(T const& x) noexcept {
          if constexpr (std::is_trivially_copyable_v<T>
          || std::is_trivially_move_constructible_v<T>
          || std::is_trivially_copy_assignable_v<T>
          || std::is_trivially_move_assignable_v<T>
          || std::is_trivially_destructible_v<T>)
              return std::true_type();
          else
              return false;
      }
      

      根据需要添加重载。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-03-01
        • 1970-01-01
        • 2015-07-21
        • 2011-11-28
        • 2012-10-16
        • 2018-05-06
        • 1970-01-01
        相关资源
        最近更新 更多