Как предотвратить создание компилятором C++ любого члена класса по умолчанию?

Я разрабатываю несколько классов для доступа и управления периферийными устройствами микроконтроллера (АЦП, порт, USB и т. Д.). Устройство имеет всего несколько (в некоторых случаях только один) экземпляров каждого периферийного устройства, поэтому я решил представить каждое периферийное устройство в виде класса с одним состоянием. Определение и использование одного из моих классов будет примерно таким:

usart.h

class usart {
public:
    static void init() { /* initialize the peripheral */ } 
    static char read() { /* read a char from the input buffer */ }
    static void write(char ch) { /* write a char to the output buffer */ }
    // ... more member functions
};

main1.cpp

#include "usart.h"

int main()
{
    usart::init();

    char data;
    while (true) {
        data = usart::read();
        usart::write(data);
    }

}

Но способ, которым класс usart определен выше, не запрещает пользователю делать что-то вроде этого:

main2.cpp

#include "usart.h"

int main() 
{
    // I don't want object construction
    usart serial1;
    usart serial2;

    // neither assignment
    serial1 = serial2;

    // two objects representing the same hardware resource
    // I don't want that
    serial1.init();
    serial2.write('r');
}

Я знаю, начиная с C++11, я могу использовать ключевое слово delete, чтобы запретить компилятору создавать конструкторы и функции по умолчанию, но я точно не знаю, какие именно значения по умолчанию создает компилятор. Существуют конструкторы копирования, назначения копирования, перегрузки семантики перемещения и т. Д. Сколько удалений мне нужно поместить в мой класс (и в какие функции и конструкторы)?

Обновление: я знаю, что мог (и, возможно, должен) использовать пространства имен вместо классов, но боюсь, позже мне нужно будет передать эти классы (или пространства имен) в качестве аргументов шаблона. AFAIK: невозможно использовать пространства имен в качестве аргументов шаблона, из-за этого я решил использовать классы со статическими членами вместо пространств имен.

2 ответа

struct cannot_exist {
  cannot_exist()=delete;
  ~cannot_exist()=delete;
  cannot_exist(cannot_exist const&)=delete;
  cannot_exist(cannot_exist &&)=delete;
  cannot_exist& operator=(cannot_exist const&)=delete;
  cannot_exist& operator=(cannot_exist &&)=delete;
};

это класс с каждым членом, который C++ генерирует для вас, явно удаленным. (Вы можете сделать это с меньшим количеством, но я не вижу смысла быть менее явным).

Просто наследовать от cannot_exist и... экземпляр вашего класса не может существовать, и при этом у него не будет никакой функции-члена, автоматически определяемой компилятором. Попытки вызвать их приведут к ошибкам компилятора.

Однако, если у вас есть класс, который не может существовать, рассмотрите возможность использования namespace,

class usart:cannot_exist {
public:
  static void init() { /* initialize the peripheral */ } 
  static char read() { /* read a char from the input buffer */ }
  static void write(char ch) { /* write a char to the output buffer */ }
  // ... more member functions
};

против

namespace usart {
  static void init() { /* initialize the peripheral */ } 
  static char read() { /* read a char from the input buffer */ }
  static void write(char ch) { /* write a char to the output buffer */ }
  // ... more member functions
};

Я бы попробовал эти два способа

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

  2. Объявите конструктор по умолчанию как закрытую функцию-член. Таким образом, метод внешнего класса не может получить доступ к конструктору.

Другие вопросы по тегам