运行 来自 class 的非默认构造函数 - 重载新运算符

Running non-default constructor from class-overloaded new operator

我想要 class Watchdog 在 POSIX 共享内存中创建它的对象。

请记住,此 class 的源代码被编译成静态库,方法的源代码实际上放在 c++ 源文件中。

我的第一次尝试是在我的 class 中有一个 static Watchdog* createWatchdog(param p1, param p2, ...) 函数,并将其用作分配对象内存的包装器,然后调用放置 new (ptr) Watchdog .

这很好用,但后来我想起 new 运算符可能会过载,所以我想出了这个解决方案:

Watchdog.h

#pragma once

#include <string>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

namespace og {

class Watchdog {
public:
    Watchdog();
    Watchdog(const std::string t_name, void* t_data, const size_t t_datSize);

    static void* operator new(size_t count, const std::string t_name, const size_t t_datSize);


protected:
    Watchdog(const Watchdog&) = delete;
    Watchdog& operator=(const Watchdog&) = delete;

private:
    std::string m_name;
    void* m_data;
    size_t m_datSize;

    // and other member variables
}; // end class Watchdog

} // end namespace

Watchdog.cxx

#include "Watchdog.h"

using namespace og;

Watchdog::Watchdog()
{}

Watchdog::Watchdog(const std::string t, void* t_data, const size_t size)
{}

void* operator new(size_t count, const std::string t_name, const size_t dat)
{
     int fd;
     void* obj;

     // removed all error checks for the sake of the example
     fd = shm_open(t_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0600);
     (void) ftruncate(fd, sizeof(Watchdog));
     obj = mmap(0x0, sizeof(Watchdog), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     (void) shm_unlink(t_name.c_str());

     return obj;
 }

CMakeLists.txt

CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
PROJECT( WATCHDOG C CXX )

SET( CMAKE_CXX_STANDARD 11)
SET( CMAKE_CXX_STANDARD_REQUIRED OFF)

SET( watchdog_HEADERS
 Watchdog.h
)

SET( watchdog_SRC
 Watchdog.cxx
 ${watchdog_HEADERS}
)

SET( CMAKE_CXX_FLAGS_DEBUG "-g3 -Wall -Wextra -Wpedantic")

ADD_LIBRARY( watchdog STATIC ${watchdog_SRC} )
TARGET_LINK_LIBRARIES( watchdog -lrt )

编译为 cmake . && make

main.cxx

#include "Watchdog.h"

using namespace og;

int
main()
{
    Watchdog* W;

    W = new ("test", 20) Watchdog;
}

  1. 使用 g++ main.cxx -L. -lwatchdog -lrt 编译会引发链接错误:

/usr/lib64/gcc/x86_64-suse-linux/9/../../../../x86_64-suse-linux/bin/ld: /tmp/ccDncNvb.o: in function `main': main.cxx:(.text+0x3c): undefined reference to `og::Watchdog::operator new(unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, unsigned long)' collect2: error: ld returned 1 exit status


  1. 是否必须在operator new方法中手动初始化所​​有成员变量? 我知道我可以通过 operator new 方法执行 return ::operator new (sz)return ::operator new(sz, ptr) 但我看不出如何调用非默认构造函数(例如 class 规范中的第二个).

问题 1:

你的定义

void* operator new(size_t count, const std::string t_name, const size_t dat) //...

Watchdog.cxx 中定义了一个自由的用户定义 operator new,而不是您在 class Watchdog 中声明的 class 特定重载。

改为使用:

void* Watchdog::operator new(size_t count, const std::string t_name, const size_t dat) //...

由于在 main 中为 new 表达式 选择了 class 特定的重载,因此将丢失其定义。


问题 2:

operator new 只有 returns 一个指向原始内存的指针。它不负责在其中构造对象。

对象由 new-expression 构造,必要时调用构造函数。语法遵循变量定义的语法。例如

W = new ("test", 20) Watchdog;

调用 Watchdog 对象的默认构造函数,而

W = new ("test", 20) Watchdog(arg1, arg2, arg3);

将尝试调用匹配三个参数的构造函数重载。

new-expression 中的第一个参数列表不用作构造函数的参数,而是分配函数需要的参数,以便提供正确的内存指针可以在其中构造对象。


还要注意,operator new 应该为(至少)count(第一个参数)字节分配内存。此参数将提供由 new-expression 构造对象所需的正确值。当您使用 operator newnew.

的数组版本时,这一点尤为重要

另请注意,您仅在 operator new 重载提供的共享内存中构造 Watchdog 对象 自身 。如果您的 class 使用 new 来分配内存,例如void* m_data 成员,它不会使用您的 operator new 进行分配,而是使用常规分配函数,它将(像往常一样)在进程(非共享)内存中分配 space.

这同样适用于 class 的非平凡成员分配的内存,例如 std::string m_name。当它需要分配内存时(如果 SSO 不足),它将使用常规分配函数(在非共享内存中)为字符串数据分配内存,而不是您的 operator new 重载。

这意味着,例如,m_name 无法从与您共享内存的其他进程安全地使用。