이 페이지에서

변경 사항 Qt Core

Qt 6의 변경 사항은 프레임워크를 더 효율적이고 사용하기 쉽게 만들기 위한 의도적인 노력의 결과입니다.

저희는 각 릴리스에서 모든 공개 API에 대해 바이너리 및 소스 호환성을 유지하기 위해 노력하고 있습니다. 하지만 Qt를 더 나은 프레임워크로 만들기 위한 노력의 일환으로 일부 변경 사항은 불가피했습니다.

이 주제에서는 Qt Core 의 변경 사항을 요약하고, 이를 처리하기 위한 지침을 제공합니다.

컨테이너 클래스

QHash, QMultiHash, QSet

qHash() 시그니처

사용자 정의 유형의 경우, ` QHash ` 및 ` QMultiHash `는 사용자가 동일한 네임스페이스 내에 ` custom qHash() function `를 제공해야 합니다. Qt 4 및 Qt 5에서 ` qHash ` 함수의 반환 값과 선택적 두 번째 인자는 ` uint` 유형이었습니다. Qt 6에서는 ` size_t` 유형입니다.

즉, 다음을 변경해야 합니다.

uint qHash(MyType x, uint seed);

다음과 같이 변경해야 합니다.

size_t qHash(MyType x, size_t seed);

이를 통해 64비트 플랫폼에서 ` QHash`, ` QMultiHash ` 및 ` QSet `가 2^32개 이상의 항목을 저장할 수 있게 되었습니다.

참조의 안정성

Qt 6에서 QHash, QMultiHash 및 QSet 의 구현 방식이 노드 기반 방식에서 2단계 조회 테이블 방식으로 변경되었습니다. 이 설계는 해시 인스턴스의 메모리 오버헤드를 매우 작게 유지하면서 동시에 우수한 성능을 제공합니다.

주목해야 할 동작상의 변경 사항 중 하나는, 테이블을 확장해야 하거나 항목이 제거될 때 새로운 구현 방식이 해시 내 요소에 대한 안정적인 참조를 제공하지 않는다는 점입니다. 이러한 안정성에 의존하는 애플리케이션은 이제 정의되지 않은 동작을 보일 수 있습니다.

QHash::insertMulti 제거

Qt 5에서는 ` QHash `를 사용하여 `QHash::insertMulti`를 통해 다중 값 해시를 생성할 수 있었으며, ` QMultiHash `는 ` QHash`에서 파생되었습니다.

Qt 6에서는 두 유형과 사용 사례가 서로 구분되며, QHash::insertMulti가 제거되었습니다.

QVector, QList

Qt 6 이전에는 QVector 와 QList 가 별도의 클래스였습니다. Qt 6에서는 이 두 클래스가 통합되었습니다. Qt 5의 QList 구현은 사라졌으며, 두 클래스 모두 업데이트된 QVector 구현을 대신 사용합니다. QList 는 실제 구현을 포함하는 클래스이며, QVector 는 QList 에 대한 별칭(typedef)입니다.

QListQt 6에서는 ` QVector`의 `fromVector()` 및 `toVector()`와 ` `의 `fromList()` 및 `toList()`가 더 이상 데이터 복사를 수행하지 않습니다. 이제 이 메서드들은 호출된 대상 객체를 그대로 반환합니다.

API 변경 사항

QList의(따라서 QVector 의) size 유형이 int 에서 qsizetype 로 변경되었습니다. size 유형과 함께, 모든 관련 메서드의 시그니처가 qsizetype 를 사용하도록 업데이트되었습니다. 이를 통해 QList 는 64비트 플랫폼에서 2^31개 이상의 항목을 저장할 수 있게 되었습니다.

코드 베이스를 Qt 6으로 업그레이드할 때, 이 API 변경으로 인해 타입 변환의 좁힘(narrowing)에 대한 컴파일러 경고가 발생할 가능성이 높습니다. 다음과 같은 예제 코드가 있다고 가정해 보겠습니다:

void myFunction(QList<MyType> &data) {
    int size = data.size();
    // ...
    const int pos = getInsertPosition(size);
    data.insert(pos, MyType());
    // ...
}

qsizetype 를 사용하거나 auto 키워드를 사용하도록 코드를 업데이트해야 합니다:

void myFunction(QList<MyType> &data) {
    auto size = data.size();
    // ...
    const auto pos = getInsertPosition(size);
    data.insert(pos, MyType());
    // ...
}

또는 형 변환을 사용하여 모든 값을 int 또는 qsizetype 로 변환할 수도 있습니다.

참고: Qt 5와 Qt 6 모두에서 빌드하려는경우 , auto 키워드를 사용하면 버전 간 시그니처 차이를 처리하는 데 효과적입니다.

메모리 레이아웃

QList 는 Qt 6에서 메모리 레이아웃과 관련된 여러 변경 사항을 거쳤습니다.

Qt 5에서는 ` sizeof(QList<T>) `의 크기가 포인터 하나 분량과 같았습니다. 이제 불필요한 포인터 간접 참조가 제거되었으며, ` QList ` 데이터 멤버는 객체에 직접 저장됩니다. 기본적으로 ` sizeof(QList<T>) `의 크기는 포인터 3개 분량과 같을 것으로 예상하십시오.

동시에 요소의 메모리 레이아웃도 업데이트되었습니다. QList 는 이제 할당된 메모리 영역에 요소를 항상 직접 저장하는 반면, Qt 5에서는 특정 객체가 힙에 별도로 할당되고 객체에 대한 포인터가 QList 에 대신 배치되었습니다.

특히 후자의 방식은 대용량 객체에 영향을 미친다는 점에 유의하십시오. Qt 5와 동일한 동작을 원한다면, 객체를 스마트 포인터로 래핑한 후 이 스마트 포인터를 QList 에 직접 저장할 수 있습니다. 이 경우, QList 의 유형은 Qt 5의 QList<MyLargeObject> 와 달리 QList<MySmartPointer<MyLargeObject>> 가 됩니다.

참조의 안정성

QVector/QList 구현에 몇 가지 변경 사항이 있습니다. QVector 와 관련된 변경 사항은 다음과 같습니다: 맨 앞쪽 삽입이 최적화되었습니다(Qt 5의 QList 와 유사하게). QList 와 관련된 변경 사항은 다음과 같습니다: 요소의 메모리 레이아웃이 단순화되었습니다.

중요: 이러한 변경 사항은 참조의 안정성에 영향을 미칩니다. Qt 6에서는 QList 가 암시적으로 공유되지 않는 경우라도, 크기나 용량을 수정하는 메서드를 호출할 때마다 모든 참조를 무효화해야 합니다. 이 규칙에 대한 예외 사항은 문서에 명시적으로 기재되어 있습니다.

특정 참조의 안정성에 의존하는 애플리케이션은 Qt 6으로 업그레이드할 때 정의되지 않은 동작이 발생할 수 있습니다. 원래 C 호환되지 않는 배열 레이아웃을 가진 ` QVector ` 또는 ` QList `가 사용된 경우에는 각별히 주의해야 합니다.

Qt6의 뷰 클래스

일반 개요

Qt6에는 몇 가지 새로운 View 클래스가 추가되었습니다. 기존에 있던 QStringView 외에도, 이제 QByteArrayView 가 추가되었으며, 이에 이어 특화된 QUtf8StringView 와 보다 범용적인 QAnyStringView 가 제공됩니다.

QStringView를 예로 든 뷰 클래스 소개

QStringView 클래스는 QString API의 읽기 전용 하위 집합을 사용하여 UTF-16 문자열에 대한 통합된 뷰를 제공합니다. 문자열의 자체 복사본(참조 카운트가 적용될 수 있음)을 유지하는 QString 와 달리, QStringView 는 다른 곳에 저장된 문자열에 대한 뷰를 제공합니다.

char hello[]{ "Hello." };   // narrow multi-byte string literal
QString str{hello};         // needs to make a copy of the string literal
QString strToStr(str);      // atomic increment involved to not create a copy of hello again

// The above code can be re-written to avoid copying and atomic increment.

QStringView view{ u"Hello." };  // view to UTF-16 encoded string literal
QStringView viewToView{ view }; // view of the same UTF-16 encoded string literal

문자열 "Hello." 는 바이너리에 저장되어 있으며 런타임에 할당되지 않습니다. view 는 문자열 "Hello." 에 대한 뷰일 뿐이므로, 별도의 복사본을 생성할 필요가 없습니다. QStringView 를 복사할 때, viewToView 는 원본 view 가 관찰하고 있는 것과 동일한 문자열을 관찰합니다. 즉, viewToView 는 복사본을 생성하거나 원자적 증분을 수행할 필요가 없습니다. 이들은 기존 문자열 "Hello." 에 대한 뷰입니다.

함수 인자로서의 뷰

뷰는 const 참조가 아닌 값으로 전달되어야 합니다.

void myfun1(QStringView sv);        // preferred
void myfun2(const QStringView &sv); // compiles and works, but slower

뷰 조작 함수

QStringView 는 문자열 뷰를 조작할 수 있는 함수를 지원합니다. 이를 통해 뷰 대상 문자열의 부분 복사본을 생성하지 않고도 뷰를 변경할 수 있습니다.

QString pineapple = "Pineapple";
QString pine = pineapple.left(4);

// The above code can be re-written to avoid creating a partial copy.

QStringView pineappleView{ pineapple };
QStringView pineView = pineappleView.left(4);

널로 끝나지 않는 문자열 및 '\0'

QStringView 널 종결 문자열과 널 종결되지 않은 문자열을 모두 지원합니다. 차이점은 ` QStringView`를 초기화하는 방식에서 비롯됩니다:

QChar aToE[]{ 'a', 'b', 'c', 'd', 'e' };

QStringView nonNull{ aToE, std::size(aToE) }; // with length given
QStringView nonNull{ aToE }; // automatically determines the length

QChar fToJ[]{ 'f', 'g', 'h', '\0', 'j' };

// uses given length, doesn't search for '\0', so '\0' at position 3
// is considered to be a part of the string similarly to 'h' and 'j
QStringView nonNull{ fToJ, std::size(fToJ) };
QStringView part{ fToJ }; //stops on the first encounter of '\0'

뷰의 소유권 모델

views 는 참조하는 메모리를 직접 소유하지 않으므로, 모든 코드 경로에서 참조된 데이터(예: QString 가 소유하는 데이터)가 view 보다 오래 유지되도록 주의해야 합니다.

QStringView sayHello()
{
    QString hello("Hello.");
    return QStringView{ hello }; // hello gets out of scope and destroyed
}

void main()
{
    QStringView hello{ sayHello() };
    qDebug() << hello; // undefined behavior
}

QStringView를 QString으로 변환하기

QStringView 는 암시적이든 명시적이든 QString 로 변환되지 않지만, 해당 데이터의 심층 복사본을 생성할 수 있습니다:

void print(const QString &s) { qDebug() << s; }

void main()
{
    QStringView string{ u"string"};

    // print(string); // invalid, no implicit conversion
    // QString str{ string }; // invalid, no explicit conversion

    print(string.toString());
    QString str = string.toString(); // create QString from view
}

중요 사항

새로운 뷰 클래스를 활용하면 많은 사용 사례에서 상당한 성능 향상을 얻을 수 있습니다. 하지만 몇 가지 주의할 점이 있을 수 있다는 점을 알아두는 것이 중요합니다. 따라서 다음 사항을 명심해야 합니다:

  • 뷰는 const 참조가 아닌 값으로 전달되어야 합니다.
  • 음수 길이로 뷰를 생성하는 것은 정의되지 않은 동작입니다.
  • 모든 코드 경로에서 참조되는 데이터(예: ` QString`가 소유한 데이터)가 뷰보다 오래 유지되도록 주의해야 합니다.

QStringView 클래스

Qt6부터는 일반적으로 QStringRef 보다 QStringView 를 사용하는 것이 권장됩니다. QStringView 는 자신이 소유하지 않은 UTF-16 문자열의 연속된 부분을 참조합니다. 이 클래스는 QString 를 먼저 생성할 필요 없이 모든 종류의 UTF-16 문자열에 대한 인터페이스 유형 역할을 합니다. QStringView 클래스는 QString 및 이전에 존재했던 QStringRef 클래스의 거의 모든 읽기 전용 메서드를 노출합니다.

참고: 참조되는 문자열 데이터(예: QString 가 소유한 데이터)가 모든 코드 경로에서 QStringView 보다 오래 유지되도록주의해야 합니다.

참고: QStringView 가 QString 를 래핑하는경우 , QStringRef 와 달리 QStringView 는 QString 데이터가 재배치된 후 내부 데이터 포인터를 업데이트하지 않으므로 주의해야 합니다.

QString string = ...;
QStringView view{string};

// Appending something very long might cause a relocation and will
// ultimately result in a garbled QStringView.
string += ...;

QStringRef 클래스

Qt6에서 QStringRef 는 Qt Core 에서 제거되었습니다. 전체 코드베이스를 수정하지 않고 기존 애플리케이션의 이식을 용이하게 하기 위해, QStringRef 클래스는 완전히 사라지지 않고 Qt5Compat 모듈로 이동되었습니다. QStringRef 를 계속 사용하려면 Qt5Compat 모듈 사용을 참조하십시오.

안타깝게도, QString 에서 노출되어 QStringRef 를 반환하는 일부 메서드는 Qt5Compat으로 이동할 수 없었습니다. 따라서 일부 수동 포팅이 필요할 수 있습니다. 코드에서 다음 함수 중 하나 이상을 사용하는 경우, QStringView 또는 QStringTokenizer 를 사용하도록 포팅해야 합니다. 또한 성능이 중요한 코드의 경우 QStringView::split 대신 QStringView::tokenize 를 사용하는 것이 권장됩니다.

QStringRef 를 사용하는 코드를 다음과 같이 변경하십시오:

QString string = ...;
QStringRef left = string.leftRef(n);
QStringRef mid = string.midRef(n);
QStringRef right = string.rightRef(n);

QString value = ...;
const QVector<QStringRef> refs = string.splitRef(' ');
if (refs.contains(value))
    return true;

다음과 같이 변경하십시오:

QString string = ...;
QStringView left = QStringView{string}.left(n);
QStringView mid = QStringView{string}.mid(n);
QStringView right = QStringView{string}.right(n);

QString value = ...;
const QList<QStringView> refs = QStringView{string}.split(u' ');
if (refs.contains(QStringView{value}))
    return true;
// or
const auto refs = QStringView{string}.tokenize(u' ');
for (auto ref : refs) {
    if (ref == value)
        return true;
}

Qt 6에서 QRecursiveMutex 는 더 이상 QMutex 를 상속받지 않습니다. 이 변경은 QMutex 와 QRecursiveMutex 의 성능을 모두 향상시키기 위해 이루어졌습니다.

이러한 변경 사항으로 인해 QMutex::RecursionMode 열거형이 제거되었으며, QMutexLocker 는 이제 QMutex 와 QRecursiveMutex 모두에서 작동할 수 있는 템플릿 클래스가 되었습니다.

QFuture 클래스

QFuture 의 의도하지 않은 사용을 방지하기 위해, Qt 6에서는 QFuture API에 몇 가지 변경 사항이 있었으며, 이로 인해 소스 호환성이 깨질 수 있습니다.

QFuture와 다른 유형 간의 암시적 변환

QFuture<T> 에서 T 로의 변환이 비활성화되었습니다. 형변환 연산자는 QFuture::result()을 호출했는데, 사용자가 변환을 시도하기 전에 QFuture 의 결과를 QFuture::takeResult()을 통해 이동시킨 경우 정의되지 않은 동작이 발생할 수 있습니다. QFuture<T> 를 T 로 변환해야 하는 경우, QFuture::result() 또는 QFuture::takeResult() 메서드를 명시적으로 사용하십시오.

QFuture<T> 에서 QFuture<void> 로의 암시적 변환도 비활성화되었습니다. 정말로 변환을 수행하려는 경우, 명시적인 QFuture<void>(const QFuture<T> &) 생성자를 사용하십시오:

QFuture<int> future = ...
QFuture<void> voidFuture = QFuture<void>(future);

등호 연산자

QFuture 의 등가 연산자가 제거되었습니다. 이 연산자들은 결과값을 비교하는 대신 기본이 되는 d-포인터를 비교했는데, 이는 사용자가 기대하는 동작과 달랐습니다. QFuture 객체를 비교해야 하는 경우, QFuture::result() 또는 QFuture::takeResult() 메서드를 사용하십시오. 예를 들어:

QFuture<int> future1 = ...;
QFuture<int> future2 = ...;
if (future1.result() == future2.result())
    // ...

QFuture 및 QFutureWatcher의 동작 변경 사항

Qt 6에서는 QFuture 및 QFutureWatcher 에 몇 가지 개선 사항이 적용되어 다음과 같은 동작 변경이 발생했습니다:

  • QFuture 또는 QFutureWatcher 을 일시 중지한 후( pause() 또는 setPaused(true) 호출 시), QFutureWatcher 는 진행 상황 및 결과 준비 신호의 전달을 즉시 중단하지 않습니다. 일시 중지 시점에는 여전히 진행 중이며 중지할 수 없는 계산이 남아 있을 수 있습니다. 이러한 계산에 대한 신호는 일시 중지 후에도 전달될 수 있으며, 다음 재개 시점에야 보고되도록 연기되지는 않습니다. 일시 중지가 실제로 적용되었을 때 알림을 받으려면 QFutureWatcher::suspended() 신호를 사용할 수 있습니다. 또한, QFuture 가 일시 중지 중인 상태인지, 아니면 이미 일시 중지된 상태인지 확인하기 위한 새로운 isSuspending() 및 isSuspended() 메서드가 있습니다. 일관성을 위해, QFuture 와 QFutureWatcher 모두에서 일시 중지 관련 API는 더 이상 사용되지 않게 되었으며, 대신 이름에 "suspend"가 포함된 유사한 메서드로 대체되었음을 유의하십시오.
  • QFuture::waitForFinished()는 이제 QFuture 가 실행 중 상태가 아닌 즉시 종료되는 대신, 실제로 완료 상태가 될 때까지 대기합니다. 이를 통해 호출 시점에 waitForFinished() 가 아직 시작되지 않은 경우 즉시 종료되는 것을 방지합니다. QFutureWatcher::waitForFinished()에도 동일한 사항이 적용됩니다. 이 변경 사항은 QFuture 를 QtConcurrent 와 함께 사용하던 코드의 동작에는 영향을 미치지 않습니다. 문서화되지 않은 QFutureInterface 와 함께 사용하던 코드만 영향을 받을 수 있습니다.
  • QFutureWatcher::isFinished()는 이제 QFutureWatcher::finished()이 발산될 때까지 false를 반환하는 대신, QFuture 의 완료 상태를 반영합니다.

QPromise 클래스

Qt 6에서는 QFuture 의 "세터" 대응 요소로 비공식 QFutureInterface 대신 새로운 QPromise 클래스를 사용해야 합니다.

IO 클래스

QProcess 클래스

Qt 6에서는 단일 명령어 문자열을 프로그램 이름과 인수로 분할하여 해석하는 QProcess::start() 오버로드의 이름이 QProcess::startCommand()로 변경되었습니다. 그러나 단일 문자열을 받는 QProcess::start() 오버로드와 인수를 위한 QStringList 는 여전히 존재합니다. QStringList 매개변수의 기본값이 빈 리스트이므로, 문자열만 전달하는 기존 코드는 여전히 컴파일되지만, 인수를 포함하는 완전한 명령어 문자열인 경우 프로세스 실행에 실패합니다.

Qt 5.15에서는 기존 코드를 쉽게 찾아 수정할 수 있도록 해당 오버로드에 대해 사용 중단 경고를 도입했습니다:

QProcess process;

// compiles with warnings in 5.15, compiles but fails with Qt 6
process.start("dir \"My Documents\"");

// works with both Qt 5 and Qt 6; also see QProcess::splitCommand()
process.start("dir", QStringList({"My Documents"});

// works with Qt 6
process.startCommand("dir \"My Documents\"");

QProcess::pid()와 Q_PID 유형이 제거되었습니다. 네이티브 프로세스 식별자를 얻으려면 대신 QProcess::processId()를 사용하십시오. 네이티브 Win32 API를 사용하여 Q_PID의 데이터를 Win32 PROCESS_INFORMATION 구조체로 접근하는 코드는 더 이상 지원되지 않습니다.

메타 타입 시스템

QVariant 클래스

QVariant 는 모든 연산에 QMetaType 를 사용하도록 재작성되었습니다. 이로 인해 몇 가지 메서드의 동작이 변경되었습니다:

  • QVariant::isNull() 이제 ` QVariant `가 비어 있거나 ` nullptr`를 포함하는 경우에만 ` true `를 반환합니다. Qt 5에서는 `qtbase`에 속한 클래스 중 자체적으로 ` isNull ` 메서드를 가지고 있고, 해당 메서드가 `true`를 반환하는 경우에도 `true`를 반환했습니다. 기존 동작에 의존하는 코드는 포함된 값이 isNull을 반환하는지 확인해야 합니다. 하지만 isNull() 는 일반적으로 관심 대상 속성이 아니기 때문에( QString::isEmpty() / isNull() 및 QTime::isValid / isNull 참조), 실제로 이러한 코드가 등장할 가능성은 희박합니다.
  • QVariant::operator== Qt 6에서는 QMetaType::equals 를 사용합니다. 따라서 적절한 등가 연산자가 없는 일부 유형(예: QPixmap 또는 QIcon)은 절대 동일하다고 비교되지 않습니다. 또한, QVariant 에 저장된 부동 소수점 수는 더 이상 qFuzzyCompare 로 비교되지 않고, 대신 정확한 비교가 사용됩니다.

또한, 서로 다른 변형(variant)은 항상 순서 지을 수 있는 것은 아니기 때문에 QVariant::operator<, QVariant::operator<=, QVariant::operator>, QVariant::operator>=가 제거되었습니다. 이는 또한 QVariant 를 QMap 의 키로 더 이상 사용할 수 없음을 의미합니다.

QMetaType 클래스

Qt 6에서는 비교자 및 QDebug, QDataStream 스트리밍 연산자의 등록이 자동으로 수행됩니다. 따라서 QMetaType::registerEqualsComparator(), QMetaType::registerComparators(), qRegisterMetaTypeStreamOperators() 및 QMetaType::registerDebugStreamOperator() 는 더 이상 존재하지 않습니다. Qt 6으로 이식할 때는 이러한 메서드에 대한 호출을 제거해야 합니다.

타입 등록

Q_PROPERTY 에서 사용되는 타입의 메타타입은 해당 클래스의 ` QMetaObject`에 저장됩니다. 이로 인해 moc이 타입을 인식할 때 타입이 완전한 상태여야 하며, 이로 인해 Qt 5에서는 정상적으로 작동하던 코드에서도 컴파일 오류가 발생할 수 있습니다. 이 문제를 해결하는 방법은 세 가지가 있습니다:

  • 해당 타입을 정의하는 헤더 파일을 포함시킵니다.
  • 헤더를 포함하는 대신 Q_MOC_INCLUDE 매크로를 사용합니다. 이는 헤더를 포함하면 순환 의존성이 발생하거나 컴파일 속도가 느려지는 경우에 도움이 됩니다.
  • 클래스를 구현하는 cpp 파일에 헤더가 이미 포함되어 있다면, 해당 위치에 moc가 생성한 파일을 포함시키는 것도 가능합니다.

정규 표현식 클래스

QRegularExpression 클래스

Qt 6에서는 QRegExp 유형이 Qt5Compat 모듈로 이전되었으며, 이를 사용하는 모든 Qt API는 다른 모듈에서 제거되었습니다. 이를 사용하던 클라이언트 코드는 대신 QRegularExpression 를 사용하도록 포팅할 수 있습니다. QRegularExpression 는 이미 Qt 5에 존재하므로, Qt 6으로 마이그레이션하기 전에 이를 수행하고 테스트할 수 있습니다.

Qt 5에서 도입된 QRegularExpression 클래스는 Perl 호환 정규 표현식을 구현하며, 제공되는 API, 지원되는 패턴 구문, 실행 속도 측면에서 QRegExp 에 비해 크게 개선되었습니다. 가장 큰 차이점은 QRegularExpression 가 단순히 정규식을 보관할 뿐이며, 일치 여부가 요청될 때 해당 정규식이 수정되지 않는다는 점입니다. 대신, 일치 결과를 확인하고 캡처된 부분 문자열을 추출하기 위해 QRegularExpressionMatch 객체가 반환됩니다. 이는 전역 일치(global matching)와 QRegularExpressionMatchIterator 에도 동일하게 적용됩니다.

그 밖의 차이점은 아래에 요약되어 있습니다.

참고: QRegularExpression 는 Perl 호환 정규 표현식에서 사용할 수 있는 모든 기능을 지원하지 않습니다. 가장 주목할 만한 점은 캡처 그룹에 대한 중복 이름이 지원되지 않으며, 이를 사용할 경우 정의되지 않은 동작이 발생할 수 있다는 사실입니다. 이는 향후 Qt 버전에서 변경될 수 있습니다.

다른 패턴 구문

QRegExp 에서 QRegularExpression 로 정규 표현식을 이식하려면 패턴 자체를 변경해야 할 수도 있습니다.

특정 상황에서는 QRegExp 가 너무 관대하여, QRegularExpression 를 사용할 때 단순히 유효하지 않은 패턴을 허용하기도 했습니다. 이러한 패턴으로 생성된 QRegularExpression 객체는 유효하지 않으므로 이를 쉽게 감지할 수 있습니다( QRegularExpression::isValid() 참조).

다른 경우에는, QRegExp 에서 QRegularExpression 로 이식된 패턴이 의미론이 변경되었음에도 별다른 경고 없이 작동할 수 있습니다. 따라서 사용된 패턴을 검토할 필요가 있습니다. 눈에 띄는 비호환성 사례는 다음과 같습니다:

  • \xHHHH 와 같이 2자리 이상의 16진수 이스케이프를 사용하려면 중괄호가 필요합니다. \x2022 와 같은 패턴은 \x{2022} 로 이식해야 하며, 그렇지 않으면 공백(0x20) 뒤에 "22" 문자열이 오는 경우에도 일치하게 됩니다. 일반적으로, 지정된 자릿수와 관계없이 \x 이스케이프에는 항상 중괄호를 사용하는 것이 강력히 권장됩니다.
  • {,n} 와 같은 0~n 개 정량화는 의미론을 보존하기 위해 {0,n} 로 변환해야 합니다. 그렇지 않으면, \d{,3} 와 같은 패턴은 숫자 하나 뒤에 정확히 "{,3}" 라는 문자열이 오는 경우와 일치하게 됩니다.
  • QRegExp 기본적으로 유니코드를 고려한 매칭을 수행하지만, QRegularExpression 의 경우 별도의 옵션이 필요합니다. 자세한 내용은 아래를 참조하십시오.
  • QRegExp 에서 c{.}는 기본적으로 줄바꿈 문자를 포함한 모든 문자와 일치합니다. QRegularExpression 는 기본적으로 줄바꿈 문자를 제외합니다. 줄바꿈 문자를 포함하려면 QRegularExpression::DotMatchesEverythingOption 패턴 옵션을 설정하십시오.

QRegularExpression 에서 지원하는 정규 표현식 구문에 대한 개요는 PCRE(Perl 호환 정규 표현식의 참조 구현)에서 지원하는 패턴 구문을 설명하는 pcrepattern(3) 매뉴얼 페이지를 참조하십시오.

QRegExp::exactMatch()에서 이식

QRegExp::exactMatch()에서 이식한 것은 두 가지 목적을 가졌습니다. 정규 표현식을 대상 문자열과 정확히 일치시키는 것과 부분 일치를 구현하는 것이었습니다.

QRegExp의 정확한 일치에서 이식

정확한 일치는 정규 표현식이 대상 문자열 전체와 일치하는지 여부를 나타냅니다. 예를 들어, 다음 클래스들은 대상 문자열 "abc123" 에 대해 다음과 같은 결과를 반환합니다:

QRegExp::exactMatch()QRegularExpressionMatch::hasMatch()
"\\d+"falsetrue
"[a-z]+\\d+"truetrue

QRegularExpression 에서는 정확한 일치가 반영되지 않습니다. 대상 문자열이 정규 표현식과 정확히 일치하는지 확인하려면, QRegularExpression::anchoredPattern() 함수를 사용하여 패턴을 감싸면 됩니다:

QString p("a .*|pattern");

// re matches exactly the pattern string p
QRegularExpression re(QRegularExpression::anchoredPattern(p));
QRegExp의 부분 일치 기능에서 이식하기

QRegExp::exactMatch()를 사용할 때, 정확한 일치가 발견되지 않았더라도 QRegExp::matchedLength()를 호출하여 정규 표현식이 대상 문자열의 어느 정도까지 일치했는지 확인할 수 있습니다. 반환된 길이가 대상 문자열의 길이와 같다면, 부분 일치가 발견된 것으로 판단할 수 있습니다.

QRegularExpression QRegularExpression::MatchType()를 통해 부분 일치를 명시적으로 지원합니다.

전역 일치

QRegExp API의 한계로 인해 전역 일치(즉, Perl에서와 같이)를 올바르게 구현하는 것은 불가능했습니다. 특히, 0개의 문자와 일치할 수 있는 패턴(예: "a*")은 문제가 됩니다.

QRegularExpression::globalMatch()는 Perl의 전역 매칭을 올바르게 구현하며, 반환된 이터레이터를 사용하여 각 결과를 확인할 수 있습니다.

예를 들어, 다음과 같은 코드가 있다고 가정해 봅시다:

QString subject("the quick fox");

int offset = 0;
QRegExp re("(\\w+)");
while ((offset = re.indexIn(subject, offset)) != -1) {
    offset += re.matchedLength();
    // ...
}

다음과 같이 다시 작성할 수 있습니다:

QString subject("the quick fox");

QRegularExpression re("(\\w+)");
QRegularExpressionMatchIterator i = re.globalMatch(subject);
while (i.hasNext()) {
    QRegularExpressionMatch match = i.next();
    // ...
}

유니코드 속성 지원

QRegExp 를 사용할 때, \w, \d 등과 같은 문자 클래스는 해당 유니코드 속성을 가진 문자와 일치합니다. 예를 들어, \d 는 유니코드 속성 Nd (십진수 자릿수)를 가진 모든 문자와 일치합니다.

QRegularExpression 를 사용할 때, 이러한 문자 클래스는 기본적으로 ASCII 문자만 일치시킵니다. 예를 들어, \d 는 0-9 ASCII 범위 내의 문자와 정확히 일치합니다. QRegularExpression::UseUnicodePropertiesOption 패턴 옵션을 사용하여 이 동작을 변경할 수 있습니다.

와일드카드 일치

QRegularExpression 에서는 와일드카드 일치를 수행하는 직접적인 방법이 없습니다. 그러나 QRegularExpression::wildcardToRegularExpression() 메서드를 사용하면 glob 패턴을 해당 용도로 활용할 수 있는 Perl 호환 정규 표현식으로 변환할 수 있습니다.

예를 들어, 다음과 같은 코드가 있다고 가정해 봅시다:

QRegExp wildcard("*.txt");
wildcard.setPatternSyntax(QRegExp::Wildcard);

다음과 같이 재작성할 수 있습니다:

auto wildcard = QRegularExpression(QRegularExpression::wildcardToRegularExpression("*.txt"));

다만, 일부 셸 스타일의 와일드카드 패턴은 예상한 대로 변환되지 않을 수 있다는 점에 유의하십시오. 다음 예제 코드는 앞서 언급한 함수를 사용하여 단순히 변환할 경우 아무런 오류 메시지 없이 작동하지 않게 됩니다:

const QString fp1("C:/Users/dummy/files/content.txt");
const QString fp2("/home/dummy/files/content.txt");

QRegExp re1("*/files/*");
re1.setPatternSyntax(QRegExp::Wildcard);
re1.exactMatch(fp1); // returns true
re1.exactMatch(fp2); // returns true

// but converted with QRegularExpression::wildcardToRegularExpression()

QRegularExpression re2(QRegularExpression::wildcardToRegularExpression("*/files/*"));
re2.match(fp1).hasMatch(); // returns false
re2.match(fp2).hasMatch(); // returns false

이는 기본적으로 ` QRegularExpression::wildcardToRegularExpression()`가 반환하는 정규 표현식이 완전히 고정(anchored)되어 있기 때문입니다. 고정되지 않은 정규 표현식을 얻으려면 변환 옵션으로 ` QRegularExpression::UnanchoredWildcardConversion `를 전달하십시오:

QRegularExpression re3(QRegularExpression::wildcardToRegularExpression(
                           "*/files/*", QRegularExpression::UnanchoredWildcardConversion));
re3.match(fp1).hasMatch(); // returns true
re3.match(fp2).hasMatch(); // returns true

최소 일치

QRegExp::setMinimal()는 양정자의 탐욕성을 단순히 반전시켜 최소 일치(minimal matching)를 구현했습니다(QRegExp 는 *?, +? 등과 같은 지연 양정자를 지원하지 않았습니다). 반면 QRegularExpression 는 탐욕적, 지연, 소유 양정자를 모두 지원합니다. QRegularExpression::InvertedGreedinessOption 패턴 옵션은 QRegExp::setMinimal()의 효과를 재현하는 데 유용할 수 있습니다. 이 옵션이 활성화되면 양화자의 탐욕성을 반전시킵니다(탐욕적인 양화자는 지연형으로, 지연형 양화자는 탐욕형으로 바뀝니다).

캐럿 모드

QRegularExpression::AnchorAtOffsetMatchOption 일치 옵션을 사용하면 QRegExp::CaretAtOffset 동작을 모방할 수 있습니다. 다른 QRegExp::CaretMode 모드에 해당하는 기능은 없습니다.

QRegExp 클래스

Qt6에서는 Qt Core 에서 QRegExp 가 제거되었습니다. 현재 애플리케이션을 바로 이식할 수 없는 경우, 해당 코드베이스가 계속 작동하도록 Qt5Compat에 QRegExp 가 여전히 존재합니다. QRegExp 를 계속 사용하려면 Qt5Compat 모듈 사용을 참조하십시오.

QEvent 및 하위 클래스

QEvent 클래스는 다형성 클래스임에도 불구하고 복사 생성자와 할당 연산자를 정의했습니다. 가상 메서드가 포함된 클래스를 복사하면 서로 다른 클래스의 객체를 서로 할당할 때 슬라이싱(slicing) 현상이 발생할 수 있습니다. 복사와 할당은 종종 암시적으로 이루어지기 때문에, 이는 디버깅하기 어려운 문제를 야기할 수 있습니다.

Qt 6에서는 암시적 복사를 방지하기 위해 QEvent 의 하위 클래스에 대한 복사 생성자와 할당 연산자가 protected로 변경되었습니다. 이벤트를 복사해야 하는 경우, ` clone ` 메서드를 사용하십시오. 이 메서드는 ` QEvent ` 객체의 힙 할당된 복사본을 반환합니다. 복사본을 게시하지 않는 한(게시할 경우 Qt가 전달이 완료된 후 자동으로 삭제합니다), `std::unique_ptr` 등을 사용하여 복사본을 반드시 삭제해야 합니다.

QEvent 의 하위 클래스에서는 clone()을 재정의하고, 다음과 같이 protected이며 기본 구현된 복사 생성자와 할당 연산자를 선언하십시오:

class MyEvent : public QEvent
{
public:
    // ...

    MyEvent *clone() const override { return new MyEvent(*this); }

protected:
    MyEvent(const MyEvent &other) = default;
    MyEvent &operator=(const MyEvent &other) = default;
    MyEvent(MyEvent &&) = delete;
    MyEvent &operator=(MyEvent &&) = delete;
    // member data
};

MyEvent 클래스가 메모리를 할당하는 경우(예: 구현체 포인터 패턴을 통해), 사용자 정의 복사 세манти크를 구현해야 합니다.

직렬화 클래스

Qt 6에서는 표준화된 CBOR 형식을 채택함에 따라, Qt의 레거시 JSON 바이너리 형식으로 변환하거나 그 반대로 변환하는 QJsonDocument 메서드가 제거되었습니다. Qt JSON 유형은 Qt CBOR 유형으로 변환될 수 있으며, 이 유형은 다시 CBOR 바이너리 형식으로 직렬화될 수 있고 그 반대의 경우도 가능합니다. 예를 들어, QCborValue::fromJsonValue() 및 QCborValue::toJsonValue()을 참조하십시오.

여전히 바이너리 JSON 형식을 사용해야 하는 경우, Qt5Compat 모듈에서 제공하는 대체 기능을 사용할 수 있습니다. 이 기능들은 QBinaryJson 네임스페이스에서 찾을 수 있습니다. 애플리케이션에서 이 모듈을 사용하는 방법은 ‘Qt5Compat 모듈 사용’을 참조하십시오.

기타 클래스

Qt 5에서는 QCoreApplication::quit()이 QCoreApplication::exit()을 호출하는 것과 동일했습니다. 이는 단순히 메인 이벤트 루프를 종료하는 것이었습니다.

Qt 6에서는 이 메서드가 대신 닫기 이벤트를 게시하여 모든 최상위 창을 닫으려고 시도합니다. 창은 해당 이벤트를 무시함으로써 종료 과정을 자유롭게 취소할 수 있습니다.

기존의 조건부 처리 방식이 아닌 동작을 유지하려면 QCoreApplication::exit()을 호출하십시오.

QLibraryInfo::location() 및 QLibraryInfo::Location은 명명 규칙이 일관되지 않아 더 이상 사용되지 않습니다. 대신 새로운 API인 QLibraryInfo::path() 및 QLibraryInfo::LibraryPath 를 사용하십시오.

Qt State Machine Framework

Qt State Machine 는 Qt SCXML 모듈(곧 Qt State Machine로 이름이 변경될 예정)로 이동되었으므로, 더 이상 Qt Core 의 일부가 아닙니다. Qt Core 내부에는 상호 의존성이 거의 없었기 때문에 결국 이러한 결정이 내려졌습니다.

Qt5Compat 모듈 사용법

Qt5Compat 모듈을 사용하려면, 인클루드 경로에 해당 헤더 파일을 포함시킨 상태로 빌드하고 해당 라이브러리를 링크해야 합니다. qmake를 사용하는 경우, .pro 파일에 다음 내용을 추가하십시오:

QT += core5compat

cmake를 사용하여 애플리케이션이나 라이브러리를 빌드하는 경우, CMakeList.txt 파일에 다음 내용을 추가하십시오:

PUBLIC_LIBRARIES
    Qt::Core5Compat

QTextStream

QTextStream::setCodec()이 제거되었습니다. 대신 새로운 Encoding 열거형을 사용하여 QTextStream::setEncoding()을 사용하십시오.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.