변경 사항 Qt Core

Qt 6는 프레임워크를 보다 효율적이고 사용하기 쉽게 만들기 위한 의식적인 노력의 결과입니다.

각 릴리스에서 모든 공개 API에 대해 바이너리 및 소스 호환성을 유지하려고 노력합니다. 하지만 Qt를 더 나은 프레임워크로 만들기 위해 몇 가지 변경이 불가피했습니다.

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

컨테이너 클래스

QHash, QMultiHash, QSet

qHash() 서명

사용자 정의 유형의 경우 QHashQMultiHash 은 동일한 네임스페이스에 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);

따라서 QHash, QMultiHashQSet 는 64비트 플랫폼에서 2^32 개 이상의 항목을 보유할 수 있습니다.

참조의 안정성

Qt 6에서 QHash, QMultiHashQSet 의 구현은 노드 기반 방식에서 2단계 룩업 테이블로 변경되었습니다. 이 설계를 통해 해시 인스턴스의 메모리 오버헤드를 매우 작게 유지하면서 동시에 우수한 성능을 제공할 수 있습니다.

주목해야 할 한 가지 동작 변화는 테이블이 커지거나 항목이 제거될 때 새로운 구현이 해시의 요소에 대한 안정적인 참조를 제공하지 않는다는 것입니다. 이러한 안정성에 의존하는 애플리케이션에서는 이제 정의되지 않은 동작이 발생할 수 있습니다.

QHash::insertMulti 제거

Qt 5에서는 QHash 을 사용하여 다중 값 해시를 생성하는 데 QHash::insertMulti 를 사용할 수 있었고 QMultiHashQHash 에서 파생되었습니다.

Qt 6에서는 유형과 유스 케이스가 모두 구별되며, QHash::insertMulti가 제거되었습니다.

QVector, QList

Qt 6 이전에는 QVectorQList 이 별도의 클래스였습니다. Qt 6에서는 통합되었습니다: Qt 5 QList 구현은 사라지고 두 클래스 모두 업데이트된 QVector 구현을 대신 사용합니다. QList 은 실제 구현이 있는 클래스이고 QVectorQList 에 대한 별칭(typedef)입니다.

QList의 fromVector() 및 toVector(), QVector 의 fromList() 및 toList() 함수는 Qt 6에서 더 이상 데이터 복사를 포함하지 않습니다. 이제 호출된 객체를 반환합니다.

API 변경 사항

QList의 (따라서 QVector 의) 크기 유형이 int 에서 qsizetype 로 변경되었습니다. 크기 유형과 함께 모든 관련 메서드의 서명이 qsizetype 를 사용하도록 업데이트되었습니다. 따라서 QList 은 64비트 플랫폼에서 2^31 개 이상의 항목을 보유할 수 있습니다.

코드 베이스를 Qt 6로 업그레이드할 때 이 API 변경으로 인해 컴파일러에서 타입 변환이 좁아진다는 경고가 표시될 가능성이 높습니다. 다음 예제 코드가 있습니다:

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

qsizetype 또는 자동 키워드를 사용하도록 업데이트해야 합니다:

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

또는 타입 캐스팅을 사용하여 모든 것을 int 또는 qsizetype 으로 캐스팅할 수 있습니다.

참고: Qt 5와 Qt 6 모두에 대해 빌드하려는 경우 자동 키워드는 두 버전 간의 시그니처 차이를 커버하는 데 좋은 솔루션입니다.

메모리 레이아웃

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 보다 오래 유지되도록 주의를 기울여야 합니다.

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

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

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

QStringRef 클래스

Qt6에서 QStringRefQt Core 에서 제거되었습니다. 전체 코드 베이스를 건드리지 않고 기존 애플리케이션을 쉽게 포팅하기 위해 QStringRef 클래스는 완전히 사라지지 않고 대신 Qt5Compat 모듈로 이동되었습니다. QStringRef 을 더 사용하려면 Qt5Compat 모듈 사용을 참조하세요.

안타깝게도 QStringRef 을 반환하는 QString 에 의해 노출된 일부 메서드는 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 에서 상속하지 않습니다. 이 변경은 QMutexQRecursiveMutex 의 성능을 개선하기 위해 수행되었습니다.

이러한 변경으로 인해 QMutex::RecursionMode 열거형이 제거되었으며 QMutexLocker 은 이제 QMutexQRecursiveMutex 모두에서 작동할 수 있는 템플릿 클래스입니다.

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에서는 QFutureQFutureWatcher 의 일부 기능이 개선되어 다음과 같은 동작이 변경되었습니다:

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

QPromise 클래스

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

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를 사용하여 Win32 PROCESS_INFORMATION 구조체로 Q_PID의 데이터에 액세스하는 코드는 더 이상 지원되지 않습니다.

메타 유형 시스템

QVariant 클래스

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

  • QVariant::isNull() 이제 QVariant 이 비어 있거나 nullptr 을 포함하는 경우에만 true 을 반환합니다. Qt 5에서는 isNull 메서드가 있는 클래스 자체에 대해서도 참을 반환하는 경우 참을 반환했습니다. 이전 동작에 의존하는 코드는 포함된 값이 isNull을 반환하는지 확인해야 하지만 isNull() 는 관심 있는 속성이 거의 없기 때문에 실제로 이러한 코드는 발생하지 않을 것입니다( QString::isEmpty() / isNull()QTime::isValid / isNull 비교 ).
  • QVariant::operator== Qt 6에서는 QMetaType::equals 를 사용합니다. 따라서 QPixmap, QImageQIcon 와 같은 일부 그래픽 유형은 절대 동일하게 비교되지 않습니다. 또한 QVariant 에 저장된 부동 소수점 숫자는 더 이상 qFuzzyCompare 과 비교되지 않고 정확한 비교를 사용합니다.

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

QMetaType 클래스

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

타입 등록

Q_PROPERTY 에서 사용되는 타입은 클래스의 QMetaObject 에 메타 타입이 저장됩니다. 따라서 moc가 유형을 볼 때 유형이 완전해야 하므로 Qt 5에서 작동하던 코드에서 컴파일 오류가 발생할 수 있습니다. 이 문제를 해결하는 방법에는 세 가지가 있습니다:

  • 유형을 정의하는 헤더를 포함하세요.
  • include를 사용하는 대신 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 객체가 반환되어 일치 결과를 확인하고 캡처된 하위 문자열을 추출합니다. 글로벌 매칭과 QRegularExpressionMatchIterator 도 동일하게 적용됩니다.

다른 차이점은 아래에 설명되어 있습니다.

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

다른 패턴 구문

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

특정 시나리오에서 QRegExp 은 너무 관대해서 QRegularExpression 을 사용할 때 단순히 유효하지 않은 패턴을 허용했습니다. 이러한 패턴으로 작성된 QRegularExpression 객체는 유효하지 않기 때문에 쉽게 감지할 수 있습니다( QRegularExpression::isValid() 참조).

다른 경우에는 QRegExp 에서 QRegularExpression 로 포팅된 패턴이 조용히 의미를 변경할 수 있습니다. 따라서 사용된 패턴을 검토할 필요가 있습니다. 가장 주목할 만한 조용한 비호환성 사례는 다음과 같습니다:

  • \xHHHH 같은 16진수 이스케이프를 2자리 이상으로 사용하려면 중괄호를 사용해야 합니다. \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의 정확히 일치에서 포팅하기

정확히 일치하는 것은 정규식이 전체 제목 문자열과 일치하는지 여부를 나타냅니다. 예를 들어, 클래스 yield는 제목 문자열 "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 (10자리) 속성과 모든 문자를 일치시킵니다).

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

와일드카드 일치

QRegularExpression 에서 와일드카드 일치를 수행하는 직접적인 방법은 없지만, QRegularExpression::wildcardToRegularExpression() 메서드는 글로브 패턴을 해당 용도로 사용할 수 있는 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()가 반환하는 정규식이 완전히 고정되어 있기 때문입니다. 앵커링되지 않은 정규식을 얻으려면 변환 옵션으로 QRegularExpression::UnanchoredWildcardConversion 을 전달하세요:

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

최소 일치

QRegExp::setMinimal()은 단순히 한정자의 욕심을 뒤집어 최소 일치를 구현했습니다(QRegExp*?, +? 등과 같은 게으른 한정자를 지원하지 않았습니다). QRegularExpression 은 대신 욕심, 게으른, 소유격 한정자를 지원합니다. QRegularExpression::InvertedGreedinessOption 패턴 옵션은 QRegExp::setMinimal()의 효과를 모방하는 데 유용할 수 있습니다. 이 옵션을 활성화하면 수량자의 욕심을 반전시킵니다(욕심 많은 수량자는 게으른 수량자가 되고 그 반대도 마찬가지입니다).

캐럿 모드

QRegularExpression::AnchorAtOffsetMatchOption 일치 옵션은 QRegExp::CaretAtOffset 동작을 에뮬레이트하는 데 사용할 수 있습니다. 다른 QRegExp::CaretMode 모드에 해당하는 것은 없습니다.

QRegExp 클래스

Qt6에서 QRegExpQt Core 에서 제거되었습니다. 지금 당장 애플리케이션을 포팅할 수 없는 경우, QRegExp 은 Qt5Compat에 여전히 존재하여 이러한 코드 베이스가 계속 작동하도록 합니다. QRegExp 을 더 사용하려면 Qt5Compat 모듈 사용을 참조하십시오.

QEvent 및 서브클래스

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

Qt 6에서는 암시적 복사를 방지하기 위해 QEvent 서브클래스의 복사 생성자와 할당 연산자가 보호되도록 했습니다. 이벤트를 복사해야 하는 경우 QEvent 객체의 힙에 할당된 복사본을 반환하는 clone 메서드를 사용하세요. 복제본을 게시하지 않는 한, 아마도 std::unique_ptr을 사용하여 복제본을 삭제해야 합니다(이 경우 Qt는 전달된 후 삭제합니다).

QEvent 서브 클래스에서 clone()을 재정의하고 보호되고 기본적으로 구현된 복사 생성자 및 할당 연산자를 다음과 같이 선언합니다:

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 프레임워크

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

Qt5Compat 모듈 사용하기

Qt5Compat 모듈을 사용하려면 포함 경로에 해당 헤더를 포함시키고 라이브러리에 링크하여 빌드해야 합니다. qmake를 사용하는 경우 .pro 파일에 다음을 추가합니다:

QT += core5compat

cmake를 사용하여 애플리케이션이나 라이브러리를 빌드하는 경우 CMakeList.txt 에 다음을 추가합니다:

PUBLIC_LIBRARIES
    Qt::Core5Compat

© 2025 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.