Введение
QSettings предоставляет кросс-платформенный доступ к настройкам приложения (реестр Windows, .ini-файлы, plist на macOS), но прямое использование QSettings с обращением по строковым ключам по всему коду — источник опечаток и отсутствия единого источника правды о доступных настройках. В статье построим типобезопасную обёртку над QSettings с явно объявленными свойствами и значениями по умолчанию.
Концепция
Обёртка инкапсулирует QSettings и предоставляет строго типизированные методы доступа для каждой настройки приложения, со значениями по умолчанию, заданными в одном месте — это устраняет дублирование строковых ключей и значений по умолчанию по всему коду, и даёт компилятору возможность поймать опечатку в имени метода (но не в строковом ключе самого QSettings, который остаётся скрытым внутри реализации).
Пример кода
#pragma once
#include <QSettings>
#include <QString>
#include <QColor>
class AppConfig
{
public:
static AppConfig &instance()
{
static AppConfig instance;
return instance;
}
// --- Настройки окна ---
QSize windowSize() const
{
return m_settings.value("window/size", QSize(1024, 768)).toSize();
}
void setWindowSize(const QSize &size)
{
m_settings.setValue("window/size", size);
}
bool isMaximized() const
{
return m_settings.value("window/maximized", false).toBool();
}
void setMaximized(bool maximized)
{
m_settings.setValue("window/maximized", maximized);
}
// --- Настройки темы ---
QString themeName() const
{
return m_settings.value("appearance/theme", "light").toString();
}
void setThemeName(const QString &theme)
{
m_settings.setValue("appearance/theme", theme);
}
// --- Сетевые настройки ---
int requestTimeoutMs() const
{
return m_settings.value("network/timeoutMs", 5000).toInt();
}
void setRequestTimeoutMs(int timeoutMs)
{
m_settings.setValue("network/timeoutMs", timeoutMs);
}
void sync()
{
m_settings.sync();
}
private:
AppConfig() : m_settings("MyCompany", "MyApp") {}
QSettings m_settings;
};
// Использование
#include "appconfig.h"
#include <QDebug>
void applyStartupSettings()
{
auto &config = AppConfig::instance();
qDebug() << "Размер окна:" << config.windowSize();
qDebug() << "Тема:" << config.themeName();
config.setThemeName("dark");
config.sync(); // принудительная немедленная запись на диск
}
Пояснения к коду
Каждая настройка приложения представлена парой методов-геттер/сеттер с осмысленным именем (windowSize()/setWindowSize()), внутри которых строковый ключ QSettings и значение по умолчанию указаны ровно один раз — изменение значения по умолчанию или структуры ключей требует правки в одном месте, а не поиска по всему проекту. Иерархические ключи вида "window/size" группируют связанные настройки в QSettings, что отражается, например, в виде разделов в .ini-файле на платформах, использующих этот формат.
Метод sync() явно сбрасывает изменения на диск — QSettings обычно делает это автоматически при удалении объекта или периодически, но явный вызов полезен в местах, где критично гарантировать сохранение настроек немедленно (например, перед потенциально аварийным завершением).
Подводные камни
- Несогласованность типа значения по умолчанию и типа, возвращаемого
.toType(). ЕслиQSettingsхранит значение, записанное в формате, не полностью совместимом с ожидаемым типом (например, после ручного редактирования .ini-файла пользователем),.toSize()/.toInt()может вернуть не то, что ожидается, без явной ошибки — для критичных настроек стоит дополнительно валидировать прочитанные значения. - Singleton-обёртка усложняет тестирование кода, зависящего от конфигурации, аналогично проблеме, описанной для общего паттерна Singleton — для тестируемого кода предпочтительнее явно передавать ссылку на конфигурацию (или интерфейс над ней) через конструктор, а не обращаться к
AppConfig::instance()напрямую из бизнес-логики. - Отсутствие миграции настроек между версиями приложения. Если структура ключей меняется между версиями (переименование, изменение формата хранимого значения), старые установленные версии приложения могут оставить устаревшие или несовместимые значения в хранилище — нужна явная логика миграции/очистки устаревших ключей при обновлении.
- QSettings не предназначен для хранения чувствительных данных (паролей, токенов) в открытом виде — на Windows реестр и .ini-файлы не зашифрованы по умолчанию, и для секретов нужно использовать отдельное защищённое хранилище (системный keychain/credential manager), а не общий механизм настроек приложения.