본문 바로가기
개발/C++

STL 또는 Qt 컨테이너?

by MinorMan 2020. 9. 30.
반응형

<질문>

STL에 상응하는 Qt 컨테이너 (QMap, QVector 등)를 사용할 때의 장단점은 무엇입니까?

Qt를 선호하는 한 가지 이유를 알 수 있습니다.

  • Qt 컨테이너는 Qt의 다른 부분으로 전달 될 수 있습니다. 예를 들어, QVariant와 QSettings를 채우는 데 사용할 수 있습니다 (단, 키가 문자열 인 QList 및 QMap / QHash 만 허용됨).

다른 것이 있습니까?

편집 : 응용 프로그램이 이미 Qt에 의존한다고 가정합니다.


<답변1>

std : :( w) string 및 STL 컨테이너를 독점적으로 사용하고 Qt 등가물로 /에서 변환하는 것으로 시작했지만 이미 QString으로 전환했으며 Qt의 컨테이너를 점점 더 많이 사용하고 있음을 알게되었습니다.

문자열과 관련하여 QString은 std :: basic_string에 비해 훨씬 더 완전한 기능을 제공하며 완전히 유니 코드를 인식합니다. 또한 내가 많이 의존하게 된 효율적인 COW 구현을 제공합니다.

Qt의 컨테이너 :

  • Qt의 foreach 매크로 (복사를 수행함)를 사용할 때와 메타 유형 또는 신호 및 슬롯을 사용할 때 매우 유용한 QString에서와 동일한 COW 구현을 제공합니다.
  • STL 스타일 반복기 또는 Java 스타일 반복기를 사용할 수 있습니다.
  • QDataStream으로 스트리밍 가능
  • Qt의 API에서 광범위하게 사용됩니다.
  • 운영 체제 전반에 걸쳐 안정적으로 구현됩니다. STL 구현은 C ++ 표준을 따라야하지만, 원하는대로 자유롭게 수행 할 수 있습니다 (std :: string COW 논란 참조). 일부 STL 구현은 특히 나쁩니다.
  • TR1을 사용하지 않는 한 사용할 수없는 해시를 제공합니다.

QTL은 J. Blanchette에 의해 잘 요약 된 STL과 다른 철학을 가지고 있습니다. "STL의 컨테이너는 원시 속도에 최적화되어 있지만 Qt의 컨테이너 클래스는 편리함, 최소한의 메모리 사용 및 최소한의 코드 확장을 제공하도록 신중하게 설계되었습니다." 위의 링크는 QTL 구현 및 사용되는 최적화에 대한 자세한 정보를 제공합니다.


<답변2>

이것은 대답하기 어려운 질문입니다. 그것은 정말로 철학적 / 주관적 논쟁으로 귀결 될 수 있습니다.

그 말은 ...

"로마에있을 때 ... 로마인처럼하십시오"라는 규칙을 추천합니다.

즉, Qt 토지에 있다면 Qt'ians가하는 것처럼 코딩하십시오. 이것은 가독성 / 일관성 문제만을위한 것이 아닙니다. 모든 것을 stl 컨테이너에 저장하고 모든 데이터를 Qt 함수에 전달해야하는 경우 어떻게되는지 고려하십시오. Qt 컨테이너로 / 밖으로 복사하는 코드 묶음을 정말로 관리하고 싶습니까? 귀하의 코드는 이미 Qt에 크게 의존하고 있으므로 stl 컨테이너를 사용하여 더 이상 "표준"으로 만드는 것과는 다릅니다. 유용한 용도로 사용할 때마다 해당 Qt 컨테이너에 복사해야하는 경우 컨테이너의 요점은 무엇입니까?


<답변3>

Qt 컨테이너는 STL 컨테이너보다 더 제한적입니다. STL이 우수한 위치에 대한 몇 가지 예 (이 모든 것이 과거에 히트했습니다) :

  • STL은 표준화되어 있으며 모든 Qt 버전 (Qt 2에는 QList (포인터 기반) 및 QValueList (값 기반)이 있고, Qt 3에는 QPtrList 및 QValueList가 있고, Qt 4에는 이제 QList가 있으며, QPtrList와 전혀 다르지 않습니다.) 또는 QValueList). Qt 컨테이너를 사용하는 경우에도 STL 호환 API 하위 집합 (예 : push_back (), append () 아님); front (), first () 아님, ...)을 사용하여 포팅을 피하십시오. Qt2-> 3 및 Qt3-> 4 전환 모두에서 Qt 컨테이너의 변경 사항은 가장 많은 코드 변동이 필요한 항목 중 하나였습니다.
  • STL 양방향 컨테이너에는 모두 rbegin () / rend ()가있어 역방향 반복을 정방향 반복과 대칭으로 만듭니다. 모든 Qt 컨테이너에 이러한 컨테이너가있는 것은 아니므로 (연관 컨테이너에는 없습니다) 역 반복은 불필요하게 복잡합니다.
  • STL 컨테이너에는 서로 다르지만 호환되는 반복기 유형의 range-insert ()가 있으므로 std :: copy ()가 훨씬 덜 필요합니다.
  • STL 컨테이너에는 할당 자 템플릿 인수가 있으므로 Qt (s / QString / secqstring /에 필요한 QLineEdit 포크)에 비해 사용자 지정 메모리 관리가 사소합니다 (typedef 필요). EDIT 20171220 : 이것은 C ++ 11 및 C ++ 17에 따른 할당 자 설계의 발전에서 Qt를 차단합니다. 예 : John Lakos의 강연 (2 부).
  • std :: deque에 해당하는 Qt는 없습니다.
  • std :: list에는 splice ()가 있습니다. std :: list를 사용할 때마다 splice ()가 필요하기 때문입니다.
  • std :: stack, std :: queue는 기본 컨테이너를 적절하게 집계하고 QStack, QQueue처럼 상속하지 않습니다.
  • QSet은 std :: set이 아니라 std :: unordered_set과 같습니다.
  • QList는 이상합니다.

위의 대부분은 Qt에서 쉽게 해결할 수 있지만 Qt의 컨테이너 라이브러리는 현재 개발 초점이 부족한 것 같습니다.

수정 20150106 : Qt 5 컨테이너 클래스에 C ++ 11 지원을 가져 오는 데 시간을 보낸 후 작업 할 가치가 없다고 결정했습니다. C ++ 표준 라이브러리 구현에 적용되는 작업을 살펴보면 Qt 클래스가 결코 따라 잡을 수 없다는 것이 분명합니다. 우리는 지금 Qt 5.4를 출시했으며 QVector는 여전히 재 할당시 요소를 이동하지 않으며, emplace_back () 또는 rvalue-push_back ()이 없습니다 ... 우리는 최근에 QOptional 클래스 템플릿을 거부하고 대신 std :: optional을 기다리고 있습니다. . std :: unique_ptr도 마찬가지입니다. 그 추세가 계속되기를 바랍니다.


<답변4>

이러한 주장을 실제 측정 가능한 현상으로 분류 해 보겠습니다.

  • 더 가벼움 : Qt 컨테이너는 STL 컨테이너보다 적은 메모리를 사용합니다.
  • 더 안전 : Qt 컨테이너는 부적절하게 사용될 기회가 적습니다.
  • 더 쉬움 : Qt 컨테이너는 지적 부담이 적습니다.

이 맥락에서 만들어진 주장은 자바 스타일 반복이 STL 스타일보다 "쉬운"것이므로이 추가 인터페이스로 인해 Qt가 사용하기 더 쉽다는 것입니다.

자바 스타일 :

QListIterator i(list);
while (i.hasNext())
    qDebug() << i.next();

STL 스타일 :

QList::iterator i;
for (i = list.begin(); i != list.end(); ++i)
    qDebug << *i;

Java 반복기 스타일은 좀 더 작고 깔끔하다는 이점이 있습니다. 문제는 이것이 실제로 더 이상 STL 스타일이 아니라는 것입니다.

C ++ 11 STL 스타일

for( auto i = list.begin(); i != list.end(); ++i)
    qDebug << *i;

또는

C ++ 11 foreach 스타일

for (QString i : list)
    qDebug << i;

매우 간단해서 다른 것을 사용할 이유가 없습니다 (C ++ 11을 지원하지 않는 한).

그러나 내가 가장 좋아하는 것은 :

BOOST_FOREACH(QString i, list)
{
    qDebug << i;
}

따라서 우리가 볼 수 있듯이이 인터페이스는 이미 매끄럽고 능률적이며 현대적인 인터페이스 위에 추가 인터페이스를 제외하고는 아무것도 얻지 못합니다. 이미 안정적이고 사용 가능한 인터페이스 위에 불필요한 추상화 수준을 추가 하시겠습니까? "쉬운"생각이 아닙니다.

또한 Qt foreach 및 Java 인터페이스는 오버 헤드를 추가합니다. 구조를 복사하고 불필요한 수준의 간접 정보를 제공합니다. 별로 보이지 않을 수도 있지만, 그다지 간단하지 않은 인터페이스를 제공하기 위해 오버 헤드 레이어를 추가하는 이유는 무엇입니까? Java에는 연산자 오버로딩이 없기 때문에 Java에는이 인터페이스가 있습니다. C ++는 그렇습니다.

Qt가 제공하는 정당화는 암시 적이거나 문제가 아닌 암시 적 공유 문제입니다. 그러나 공유가 포함됩니다.

QVector a, b;
a.resize(100000); // make a big vector filled with 0.

QVector::iterator i = a.begin();
// WRONG way of using the iterator i:
b = a;
/*
Now we should be careful with iterator i since it will point to shared data
If we do *i = 4 then we would change the shared instance (both vectors)
The behavior differs from STL containers. Avoid doing such things in Qt.
*/

첫째, 이것은 암시 적이 지 않습니다. 한 벡터를 다른 벡터에 명시 적으로 할당합니다. STL 반복자 사양은 반복기가 컨테이너에 속함을 명확하게 나타내므로 b와 a 사이에 공유 컨테이너를 명확하게 도입했습니다. 둘째, 이것은 문제가 아닙니다. 반복자 사양의 모든 규칙을 따르는 한 절대로 잘못된 것은 없습니다. 문제가 발생하는 유일한 경우는 다음과 같습니다.

b.clear(); // Now the iterator i is completely invalid.

Qt는이 시나리오에서 새로운 문제가 발생하는 것과 같이 의미하는 것처럼 지정합니다. 그렇지 않습니다. 반복기는 무효화되고 여러 분리 된 영역에서 액세스 할 수있는 모든 것과 마찬가지로 이것이 작동하는 방식입니다. 실제로 이것은 Qt의 Java 스타일 반복자에서 쉽게 발생할 수 있습니다. 이는 암시 적 공유에 크게 의존하기 때문입니다. 이는 여기에 설명 된 것과 같은 반 패턴이며 다른 여러 영역에서도 마찬가지입니다. 이 "최적화"가 점점 더 멀티 스레딩으로 이동하는 프레임 워크에서 사용되는 것이 이상해 보이지만 그것은 당신을위한 마케팅입니다.

이것은 좀 더 까다 롭습니다. Copy-On-Write 및 암시 적 공유 및 성장 전략을 사용하면 컨테이너가 주어진 시간에 사용할 메모리 양을 실제로 보장하기가 매우 어렵습니다. 이것은 강력한 알고리즘 보장을 제공하는 STL과는 다릅니다.

벡터에 대해 낭비되는 공간의 최소 경계가 벡터 길이의 제곱근이라는 것을 알고 있지만이를 Qt에서 구현할 방법이없는 것 같습니다. 그들이 지원하는 다양한 "최적화"는이 매우 중요한 공간 절약 기능을 배제합니다. STL은이 기능을 필요로하지 않지만 (대부분은 더 낭비적인 두 배 증가를 사용합니다), 필요한 경우 최소한이 기능을 구현할 수 있다는 점에 유의하는 것이 중요합니다.

XOr 연결을 사용하여 사용 된 공간을 대폭 줄일 수있는 이중 연결 목록도 마찬가지입니다. 다시 말하지만, 성장과 COW에 대한 요구 사항으로 인해 Qt에서는 불가능합니다.

COW는 실제로 무언가를 더 가볍게 만들 수 있지만 boost 지원과 같은 Intrusive Containers도 가능하며 Qt는 이전 버전에서 자주 사용했지만 사용하기 어렵고 안전하지 않으며 부담을주기 때문에 더 이상 많이 사용되지 않습니다. 프로그래머에게. COW는 훨씬 덜 방해가되는 솔루션이지만 위에 제시된 이유로 매력적이지 않습니다.

같은 메모리 비용으로 또는 Qt의 컨테이너보다 적은 STL 컨테이너를 사용할 수없는 이유가 없으며, 주어진 시간에 얼마나 많은 메모리를 낭비할지 실제로 아는 추가 이점이 있습니다. 안타깝게도 원시 메모리 사용량에서 두 가지를 비교하는 것은 불가능합니다. 이러한 벤치 마크는 STL이 수정하도록 설계된 정확한 종류의 문제인 다른 사용 사례에서 매우 다른 결과를 보여주기 때문입니다.

복사 비용을 부과하지 않고 가능하면 Qt 컨테이너 사용을 피하고 가능하면 STL 유형 반복 (아마도 래퍼 또는 새 구문을 통해)을 사용하십시오.


<답변5>

STL 컨테이너 :

  • 성능 보장
  • 성능이 보장되는 STL 알고리즘에서 사용할 수 있습니다.
  • Boost와 같은 타사 C ++ 라이브러리에서 활용할 수 있습니다.
  • 표준이며 독점 솔루션보다 오래 지속될 가능성이 있음
  • 알고리즘 및 데이터 구조의 일반적인 프로그래밍을 장려합니다. STL을 준수하는 새로운 알고리즘과 데이터 구조를 작성하는 경우 STL이 이미 제공하는 것을 무료로 활용할 수 있습니다.

<답변6>

Qt 컨테이너는 copy-on-write 관용구를 사용합니다.


<답변7>

주요 문제 중 하나는 Qt의 API가 사용자가 Qt의 컨테이너에 데이터를 제공 할 것으로 기대하므로 둘 사이를 앞뒤로 변환하는 대신 Qt 컨테이너를 사용하는 것이 좋습니다.

또한 이미 Qt 컨테이너를 사용하고 있다면 STL 헤더 파일을 포함 할 필요가없고 잠재적으로 STL 라이브러리에 링크 할 필요가 없기 때문에이를 독점적으로 사용하는 것이 약간 더 최적 일 수 있습니다. 그러나 도구 체인에 따라 어쨌든 발생할 수 있습니다. 순전히 디자인 관점에서 보면 일관성은 일반적으로 좋은 것입니다.


<답변8>

작업중인 데이터가 주로 Qt 기반 UI를 구동하는 데 사용되는 경우 Qt 컨테이너를 사용하십시오.

데이터가 대부분 앱에서 내부적으로 사용되며 Qt에서 멀어 질 가능성이없는 경우 성능 문제를 제외하고 Qt 컨테이너를 사용하면 UI로 이동하는 데이터 비트를 더 쉽게 처리 할 수 있습니다.

데이터가 STL 컨테이너에 대해서만 알고있는 다른 라이브러리와 함께 주로 사용되는 경우 STL 컨테이너를 사용하십시오. 이 상황이 발생하면 무엇을하든 컨테이너 유형간에 많은 포팅을 수행 할 것이기 때문에 무엇을하든 문제가 발생합니다.


<답변9>

COW의 차이점 외에도 STL 컨테이너는 다양한 플랫폼에서 훨씬 더 광범위하게 지원됩니다. Qt는 작업을 "주류"플랫폼으로 제한하는 경우 충분히 이식 가능하지만 STL은 다른 많은 모호한 플랫폼 (예 : Texas Instruments의 DSP)에서도 사용할 수 있습니다.

STL은 단일 기업이 통제하는 것이 아니라 표준이기 때문에 일반적으로 STL 코드를 쉽게 읽고, 이해하고, 수정할 수있는 프로그래머와이를 지원하는 더 많은 리소스 (책, 온라인 포럼, 컨퍼런스 등)가 있습니다. Qt보다 이렇게합니다. 이 이유만으로 Qt를 피해야한다는 말은 아닙니다. 다른 모든 것들이 같으면 기본적으로 STL을 사용해야하지만 물론 모든 것이 거의 같지 않으므로 자신의 상황에서 가장 합리적인 결정을 내려야합니다.

AlexKR의 답변과 관련하여 STL 성능은 제한 내에서 보장되지만 특정 구현은 플랫폼에 따라 다른 세부 정보를 사용하여 STL 속도를 높일 수 있습니다. 따라서 그런 의미에서 플랫폼마다 다른 결과를 얻을 수 있지만 명시 적 보증 (모듈로 버그)보다 느리지는 않습니다.


<답변10>

내 5 센트 : Qt 컨테이너는 다른 플랫폼에서 비슷하게 작동해야합니다. STL 컨테이너는 STL 구현에 의존합니다. 다른 성능 결과를 얻을 수 있습니다.

편집 : STL이 "더 느리다"고 말하는 것은 아니지만 다양한 구현 세부 사항의 효과를 지적합니다. 이걸 확인하고 아마 이거. 그리고 그것은 STL의 실제 문제가 아닙니다. 분명히 성능에 큰 차이가 있다면 STL을 사용하는 코드에 문제가있는 것입니다.


<답변11>

Qt를 사용하는 방식에 따라 다릅니다. 제품 전체에 걸쳐 사용한다면 Qt 컨테이너를 사용하는 것이 합리적 일 것입니다. 예를 들어 UI 부분에만 포함하는 경우 C ++ 표준 컨테이너를 사용하는 것이 좋습니다.


<답변12>

나는 STL이 훌륭한 소프트웨어라고 생각하지만 KDE 나 Qt 관련 프로그래밍을한다면 Qt가 갈 길이다. 또한 사용하는 컴파일러에 따라 다르지만 GCC STL은 꽤 잘 작동하지만 SUN Studio CC를 사용해야하는 경우 STL은 STL 자체가 아닌 컴파일러 때문에 두통을 일으킬 가능성이 큽니다. 이 경우 컴파일러가 머리를 아프게 할 것이기 때문에 Qt를 사용하여 문제를 해결하십시오. 내 2 센트 만 ...


<답변13>

QVector에는 (때때로) 큰 제한이 있습니다. 메모리의 int 바이트 만 할당 할 수 있습니다 (제한은 요소 수가 아닌 바이트 단위입니다). 이는 QVector를 사용하여 ~ 2GB보다 큰 연속적인 메모리 블록을 할당하려고하면 충돌이 발생 함을 의미합니다. 이것은 Qt 4 및 5에서 발생합니다. std :: vector에는 이러한 제한이 없습니다.


<답변14>

나를 위해 STL 컨테이너를 사용하는 주된 이유는 매우 큰 컨테이너에서 메모리를 재사용하기 위해 사용자 지정 할당자가 필요한 경우입니다. 예를 들어 1000000 개의 항목 (키 / 값 쌍)을 저장하는 QMap이 있다고 가정하십시오. Qt에서는 정확히 1000000 만 할당 (새 호출)을 의미합니다. STL에서는 항상 내부적으로 모든 메모리를 한 번에 할당하고 맵이 채워질 때 각 항목에 할당하는 사용자 지정 할당자를 만들 수 있습니다.

내 조언은 비즈니스 로직에서 성능에 중요한 알고리즘을 작성할 때 STL 컨테이너를 사용하고 필요한 경우 UI 컨트롤 및 양식에 의해 결과가 표시 될 준비가되면이를 다시 Qt 컨테이너로 변환하는 것입니다.

728x90

댓글