Запуск нестандартного конструктора из перегруженного классом нового оператора - PullRequest
0 голосов
/ 27 октября 2019

Я хочу, чтобы класс Watchdog создавал свои объекты в разделяемой памяти POSIX.

Имейте в виду, что исходный код этого класса скомпилирован в статическую библиотеку, а исходный код для методов фактически помещен висходный файл c ++.

Моя первая попытка была иметь функцию 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


Нужно ли инициализировать все переменные-члены вручную в методе оператора new ? Я знаю, что могу сделать return ::operator new (sz) или return ::operator new(sz, ptr) из нового метода оператора, но я не вижу, как вместо этого я могу вызвать конструктор не по умолчанию (например, второй в спецификации класса).

1 Ответ

2 голосов
/ 27 октября 2019

Вопрос 1:

Ваше определение

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

в Watchdog.cxx определяет свободную пользовательскую operator new, а не перегрузку, специфичную для класса, которую вы объявили в классеWatchdog.

Вместо этого используйте:

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

Поскольку для new-expression в main выбрано переопределение класса, его определение будетотсутствует.


Вопрос 2:

operator new возвращает только указатель на необработанную память. Он не отвечает за конструирование объектов в нем.

Объекты создаются с помощью 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.


Также имейте в виду, что вы создаете только сам объект Watchdog в общей памяти, предоставляемой вашей перегрузкой operator new. Если ваш класс использует new для выделения памяти, например, для члена void* m_data, он будет использовать не operator new для выделения, а обычную функцию выделения, которая будет выделять (как обычно) в памяти процессов (неразделенной)пробел.

То же самое относится к памяти, выделенной нетривиальным членам вашего класса, таким как std::string m_name. Когда ему понадобится выделить память (если SSO недостаточно), он выделит память для строковых данных с помощью обычной функции выделения (в неразделенной памяти), а не вашей перегрузки operator new.

Этоозначает, например, что m_name нельзя безопасно использовать из другого процесса, с которым вы делитесь памятью.

...