← Назад к вопросам

Как в Qt передавать по значению?

2.2 Middle🔥 61 комментариев
#Qt и GUI#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как в Qt передавать по значению?

Это интересный вопрос, потому что в Qt есть специфические рекомендации и оптимизации для передачи данных. Ответ больше нюансов, чем кажется на первый взгляд.

Стандартный подход C++: const reference

В обычном C++ рекомендуется передавать большие объекты по const reference, а не по значению:

// ПЛОХО - копирование
void processString(std::string str) {
    std::cout << str << std::endl;
}

// ХОРОШО - без копирования
void processString(const std::string& str) {
    std::cout << str << std::endl;
}

Это сделано для избежания ненужного копирования.

Философия Qt: Copy-on-write

Qt развивалась в эру, когда Copy-on-Write (CoW) была популярна. Идея простая:

Отправитель:     QString data("Hello")
Передача:        void func(QString str)  <- копирование по значению, но...
Внутри func:     str указывает на ТЕ ЖЕ ДАННЫЕ что и data
                 Копирование "отложено" (lazy)

Изменение:       str[0] = 'J'  <- только здесь создается реальная копия
Теперь:          str указывает на "Jello", data остается "Hello"

Поэтому в Qt часто передавали по значению:

// В Qt это было OK и даже рекомендовалось
void drawShape(QBrush brush) {  // По значению
    // brush скопирован, но через CoW это дешево
}

Современный Qt (5.x и 6.x): Ситуация изменилась

В современных версиях Qt копировать дорого, потому что:

  1. Copy-on-Write убрана из большинства типов (QString, QList и т.д.)
  2. Обычное копирование теперь выделяет новую память
  3. Move семантика стала доступна

Поэтому теперь рекомендуется передавать по const reference:

// СОВРЕМЕННЫЙ Qt - рекомендуемый способ
void drawShape(const QBrush& brush) {
    // brush передана по reference - нет копирования
}

void setText(const QString& text) {
    // QString передана по reference
}

void addToList(const QStringList& items) {
    // QStringList передана по reference
}

Когда передавать по значению в Qt

1. Маленькие типы (как int, bool)

// По значению - OK, это дешево
void setWidth(int width) { /* */ }
void setEnabled(bool enabled) { /* */ }
void setOpacity(float opacity) { /* */ }

2. Когда нужна копия

// Вы берете владение объектом
void storeData(QByteArray data) {
    // Вы сохраняете копию
    m_cachedData = data;
}

// Эквивалентно:
void storeData(const QByteArray& data) {
    m_cachedData = data;  // Копирование явное
}

3. Qt сигналы и слоты

В Qt, параметры сигналов часто передаются по значению автоматически:

class MyWidget : public QWidget {
    Q_SIGNALS:
        // Параметры автоматически копируются при emit
        void dataChanged(const QString& newData);  // или QString newData
        void itemClicked(int itemId);              // по значению - OK
        
    Q_SLOTS:
        void onDataChanged(const QString& data) {  // Qt скопирует
            // data это копия от emit
        }
};

int main() {
    MyWidget widget;
    QString myData = "Hello";
    
    // Qt сделает копию и передаст в слот
    emit widget.dataChanged(myData);
}

Qt автоматически управляет копированием параметров сигналов.

Правила для современного Qt

Правило 1: Примитивные типы - по значению

void setSize(int width, int height) { }  // OK
void setColor(float r, float g, float b) { }  // OK
void setVisible(bool visible) { }  // OK

Правило 2: QString, QByteArray, QList - по const reference

// ХОРОШО
void setTitle(const QString& title) { }
void setData(const QByteArray& data) { }
void setItems(const QStringList& items) { }

// ПЛОХО (ненужное копирование)
void setTitle(QString title) { }  // Копирует!
void setData(QByteArray data) { }  // Копирует!

Правило 3: Объекты QWidget/QObject - по указателю

void setParent(QObject* parent) { }  // Указатель - не копируем
void setWidget(QWidget* widget) { }  // Указатель

Правило 4: Пользовательские классы - по const reference

struct MyData {
    QString name;
    QStringList items;
    QByteArray blob;
};

void processData(const MyData& data) {  // const reference
    // Нет копирования
}

Move семантика в Qt

Модерный Qt поддерживает move семантику:

// Если вы передаете временный объект - используется move
void setText(QString text) {  // По значению
    m_text = std::move(text);  // Эффективно
}

// Вызов с rvalue
setText(QString("Hello"));  // Временный объект - используется move!

// Вызов с lvalue
QString myString = "Hello";
setText(myString);  // Копирование (потому что myString может быть использован потом)

Есть инструмент Qt coding style который рекомендует параметр по значению ЕСЛИ функция берет владение:

// Если функция сохраняет копию
void setData(QByteArray data) {  // По значению OK, будет скопировано
    m_data = data;  // Ясно, что мы берем копию
}

// vs

void setData(const QByteArray& data) {  // По const reference
    m_data = data;  // Тоже ясно - копия с явным оператором =
}

Оба варианта валидны, но второй более явный.

Практические примеры для Qt

Пример 1: Custom widget

class MyLabel : public QLabel {
public:
    // Правильно для Qt
    void setCustomText(const QString& text) {
        setText(text);  // QString::setText берет const ref
    }
    
    void setCustomFont(const QFont& font) {
        setFont(font);  // QWidget::setFont берет const ref
    }
    
    void setCustomColor(QColor color) {  // Маленький объект
        setStyleSheet(QString("color: %1").arg(color.name()));
    }
};

Пример 2: Data storage

class DataManager {
private:
    QStringList m_items;
    QMap<QString, QVariant> m_settings;
    
public:
    // Правильно
    void addItem(const QString& item) {
        m_items.append(item);
    }
    
    void setSetting(const QString& key, const QVariant& value) {
        m_settings[key] = value;
    }
    
    const QStringList& getItems() const {
        return m_items;  // const ref - нет копирования
    }
};

Пример 3: Network operations

class NetworkHandler : public QObject {
    Q_OBJECT
    
public Q_SLOTS:
    // Qt network обычно использует const ref
    void sendRequest(const QByteArray& requestData) {
        // requestData передана по const ref
        m_socket->write(requestData);
    }
    
Q_SIGNALS:
    void dataReceived(const QByteArray& data);  // Qt скопирует при emit
};

Оптимизация: Если очень важна производительность

// Если нужна максимальная производительность для QString
void processStrings(QStringView view) {  // QStringView - очень легкий!
    // QStringView - это просто указатель на данные + размер
    // Нет копирования, даже нет ref counting
    std::cout << view.toStdString();
}

// Использование
QString str = "Hello";
processStrings(str);  // QStringView не копирует
processStrings(u"Hello");  // Работает и с литералами

Проверка примеров Qt кода

Если вы смотрите исходный код Qt:

// From Qt source code (QString methods)
void append(const QString& str);  // const reference
void append(QChar ch);            // примитив - по значению

void setStyleSheet(const QString& styleSheet);  // const reference
void setProperty(const char* name, const QVariant& value);  // const ref

void move(int x, int y);  // примитивы - по значению

Всё следует одному правилу: const ref для больших объектов, значение для мелких.

Итоговые рекомендации для Qt

  1. int, bool, float, double - по значению
  2. QString, QByteArray, QStringList, QList - по const &
  3. QWidget, QObject** - по указателю
  4. QColor, QFont, QPen - по const & (они достаточно большие)
  5. QVariant - по const &
  6. Пользовательские типы - по const &
  7. Если функция берет владение - по значению для явности (или rvalue ref)

Современная тенденция

В Qt 6.x все больше переходят на:

// Вместо:
void setTitle(const QString& title);

// Пишут:
void setTitle(QAnyStringView title);  // Работает с QString, const char*, etc

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

Финальное слово

В Qt передавайте большие объекты по const reference, если только вы не нуждаетесь в явном копировании. Это современный, эффективный и правильный подход для Qt 5.x и Qt 6.x.