运行 来自 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;
}
- 使用
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
- 是否必须在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 new
和 new
.
的数组版本时,这一点尤为重要
另请注意,您仅在 operator new
重载提供的共享内存中构造 Watchdog
对象 自身 。如果您的 class 使用 new
来分配内存,例如void* m_data
成员,它不会使用您的 operator new
进行分配,而是使用常规分配函数,它将(像往常一样)在进程(非共享)内存中分配 space.
这同样适用于 class 的非平凡成员分配的内存,例如 std::string m_name
。当它需要分配内存时(如果 SSO 不足),它将使用常规分配函数(在非共享内存中)为字符串数据分配内存,而不是您的 operator new
重载。
这意味着,例如,m_name
无法从与您共享内存的其他进程安全地使用。
我想要 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;
}
- 使用
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
- 是否必须在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 new
和 new
.
另请注意,您仅在 operator new
重载提供的共享内存中构造 Watchdog
对象 自身 。如果您的 class 使用 new
来分配内存,例如void* m_data
成员,它不会使用您的 operator new
进行分配,而是使用常规分配函数,它将(像往常一样)在进程(非共享)内存中分配 space.
这同样适用于 class 的非平凡成员分配的内存,例如 std::string m_name
。当它需要分配内存时(如果 SSO 不足),它将使用常规分配函数(在非共享内存中)为字符串数据分配内存,而不是您的 operator new
重载。
这意味着,例如,m_name
无法从与您共享内存的其他进程安全地使用。