답변:
나는 std::(w)string
STL 컨테이너를 독점적 으로 사용 하고 Qt와 동등한 것으로 변환하거나 그로부터 변환하는 것으로 시작 했지만 이미 전환했으며 QString
Qt의 컨테이너를 점점 더 많이 사용하고 있음을 알았습니다.
문자열 QString
과 관련하여 훨씬 더 완벽한 기능을 제공 std::basic_string
하며 완전히 유니 코드를 인식합니다. 또한 효율적인 COW 구현을 제공하므로 크게 의존합니다.
Qt의 컨테이너 :
QString
. 이는 Qt의 foreach
매크로 (사본 수행)를 사용하거나 메타 유형 또는 신호 및 슬롯 을 사용할 때 매우 유용 합니다.QDataStream
std::string
COW 논쟁 참조 ). 일부 STL 구현은 특히 나쁩니다.QTL은 J. Blanchette에 의해 요약 된 STL과는 다른 철학을 가지고 있습니다 . "STL의 컨테이너가 원시 속도에 최적화 된 반면, Qt의 컨테이너 클래스는 편의성, 최소 메모리 사용 및 최소 코드 확장을 제공하도록 신중하게 설계되었습니다."
위의 링크는 QTL 구현 및 사용되는 최적화에 대한 자세한 정보를 제공합니다.
QList<double>
메모리 사용을 위해 32 비트 아키텍처에서 프로파일 을 작성하십시오.
QVector
대신에 프로파일을 작성하십시오 QList
. QList는 객체에 포인터를 저장하도록 설계되었다는 꽤 많은 Qt 설명이 있습니다. 따라서 동적으로 생성 된이 두 항목과이 항목에 대한 포인터는에 저장됩니다 QList
. QList는 벡터와 링크 된 목록 사이의 "중간"컨테이너로 설계되었습니다. 메모리 / 성능이 중요한 경우를 위해 설계되지 않았습니다.
질문에 대답하기가 어렵습니다. 그것은 철학적 / 주관적 논쟁으로 귀결 될 수 있습니다.
그 말은 ...
나는 "로마에있을 때 ... 로마인들이하는 것처럼"
Qt 땅에 있다면 Qt'ians처럼 코딩하십시오. 이것은 가독성 / 일관성 문제만을위한 것이 아닙니다. stl 컨테이너에 모든 것을 저장하면 어떤 일이 발생하는지 고려한 다음 모든 데이터를 Qt 함수에 전달해야합니다. 실제로 Qt 컨테이너에 물건을 복사하는 코드 묶음을 관리하고 싶습니까? 코드는 이미 Qt에 크게 의존하므로 stl 컨테이너를 사용하여 더 "표준"으로 만드는 것과는 다릅니다. 컨테이너를 유용하게 사용할 때마다 컨테이너의 요점은 무엇입니까? 해당 Qt 컨테이너에 복사해야합니까?
Qt 컨테이너는 STL 컨테이너보다 제한됩니다. STL이 우수한 곳의 몇 가지 예 (이 모든 것이 과거에 부딪 쳤습니다) :
QList
포인터 기반) 및 ( QValueList
; Qt는 3 있었다 (가치 기반은) QPtrList
와 QValueList
Qt는 4 지금 갖고 QList
와 같은 모든에서 그것의 아무것도 QPtrList
또는 QValueList
). push_back()
하지 append()
, front()
하지 first()
, ...) 모두 Qt2-> 3 Qt3-에서 Qt는 5 와서 다시 한번 이식 피하기 위해> 4 Qt 컨테이너의 변경은 코드 변경이 가장 필요한 변경 중 하나였습니다.rbegin()
/ rend()
가 있으며 역 반복 대칭을 정방향 반복으로 만듭니다. 모든 Qt 컨테이너에 컨테이너가 포함되어 있지는 않기 때문에 (반복되는 컨테이너에는 해당되지 않음) 역 반복은 불필요하게 복잡합니다.insert()
다르지만 호환 가능한 반복자 유형의 범위 를 가지므로 std::copy()
훨씬 덜 자주 필요합니다.Allocator
템플릿 인수가있어 Qt (fork of required )와 비교하여 커스텀 메모리 관리가 간단합니다 (typedef 필요 ). EDIT 20171220 : C ++ 11 및 C ++ 17에 따라 할당 자 디자인의 진보로 인해 Qt가 줄어 듭니다 . 예 : John Lakos의 강연 ( 2 부 ).QLineEdit
s/QString/secqstring/
std::deque
.std::list
있다 splice()
. 내가 자신을 사용하는 것을 찾을 때마다 std::list
필요하기 때문 splice()
입니다. std::stack
, std::queue
로 제대로, 자신의 기본 컨테이너를 집계하고 상속하지 않습니다 QStack
, QQueue
않습니다.QSet
같은 std::unordered_set
것이 아닙니다 std::set
.QList
A는 단지 이상한 .위의 많은 것들이 Qt에서 매우 쉽게 해결할 수 있지만 Qt 의 컨테이너 라이브러리는 현재 개발 포커스가 부족한 것으로 보입니다.
EDIT 20150106 : Qt 5 컨테이너 클래스에 C ++ 11 지원을 가져 오는 데 약간의 시간을 보낸 후 작업 가치가 없다고 결정했습니다. C ++ 표준 라이브러리 구현에 적용되는 작업을 살펴보면 Qt 클래스가 결코 따라 잡지 않을 것이 분명합니다. 우리는 현재 Qt 5.4를 출시QVector
했지만 여전히 재 할당의 요소를 옮기지emplace_back()
않거나 rvalue가없거나 rvalue-입니다push_back()
... ... 또한 최근에QOptional
클래스 템플릿을거부하고std::optional
대신기다렸습니다. 마찬가지로std::unique_ptr
. 트렌드가 계속되기를 바랍니다.
QList
이었다 에 동등 std::deque
. 분명히, 나는 단지 문서를 훑어 보면 안된다.
QVector
했다 crbegin
Qt는 5.6 이후 친구
std::reverse_iterator
깨진 이상 QHash
/ QMap
, 역 참조 할 때, 반환 반복자, mapped_type
대신를 value_type
). 고칠 수없는 것은 없지만 2015 년부터 편집 한 내용을 참조하십시오 .
QVector
사용 int
하여 31 비트 크기 (64 비트 시스템에서도)를 제한 한다는 사실을 목록에 추가 할 가치가 있습니다 . 또한 INT_MAX
1 바이트보다 큰 크기의 요소 도 저장할 수 없습니다 . 예를 들어, x86_64 Linux gcc에서 .size()
가질 수 있는 가장 큰 QVector<float>
것은 536870907 요소 (2²⁹-5) 였지만 std::vector<float>
4294967295 요소 (2³²-1;이 RAM은 부족하여 더 이상 시도하지 않았습니다 (이 크기는 이미 16GiB)) ).
이러한 주장을 실제 측정 가능한 현상으로 나누겠습니다.
이 문맥에서 주장하는 것은 자바 스타일 반복이 STL 스타일보다 어떻게 든 "더 쉽다"는 것입니다. 따라서이 추가 인터페이스로 인해 Qt가 더 사용하기 쉽습니다.
자바 스타일 :
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
STL 스타일 :
QList<QString>::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<int> a, b;
a.resize(100000); // make a big vector filled with 0.
QVector<int>::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 스타일 반복자와 함께 쉽게 발생합니다. 암시 적 공유에 크게 의존하기 때문입니다. 이는 여기 및 기타 여러 영역 에서 설명한 반 패턴 입니다. 이 "최적화"가 멀티 스레딩으로 점점 더 이동하는 프레임 워크에서 사용되는 것은 특히 이상해 보이지만, 그것은 당신을위한 마케팅입니다.
이것은 조금 까다 롭습니다. 쓰기시 복사 및 암시 적 공유 및 성장 전략을 사용하면 컨테이너가 특정 시간에 사용할 메모리 양을 실제로 보장하기가 매우 어렵습니다. 이것은 강력한 알고리즘 보장을 제공하는 STL과 다릅니다.
우리 는 벡터의 최소 낭비 공간이 벡터 길이의 제곱근이라는 것을 알고 있지만 Qt에서는 이것을 구현할 방법이없는 것 같습니다. 그들이 지원하는 다양한 "최적화"는이 중요한 공간 절약 기능을 배제 할 것입니다. STL은이 기능을 필요로하지 않으며 (이는 대부분 두 배의 성장을 사용하므로 더 낭비입니다) 필요한 경우 최소한이 기능을 구현할 수 있다는 점에 유의해야합니다.
XOr 연결을 사용하여 사용 된 공간을 크게 줄일 수있는 이중 연결 목록도 마찬가지입니다. 다시 말하지만, 이것은 성장과 COW에 대한 요구로 인해 Qt에서는 불가능합니다.
COW는 실제로 더 가벼운 것을 만들 수 있지만 boost 에서 지원하는 것과 같은 Intrusive Containers도 가능 하며 Qt는 이전 버전에서 자주 사용했지만 더 이상 사용하기 어렵고 안전하지 않으며 부담을 가하기 때문에 더 이상 사용되지 않습니다. 프로그래머에. COW는 훨씬 덜 관입적인 솔루션이지만 위에 제시된 이유로 매력이 없습니다.
메모리 비용이 같거나 Qt의 컨테이너보다 적은 STL 컨테이너를 사용할 수없는 이유는 없으며, 주어진 시간에 얼마나 많은 메모리를 낭비하는지 실제로 알 수 있다는 이점이 있습니다. 불행히도, 원시 메모리 사용량에서 두 가지를 비교하는 것은 불가능합니다. 그러한 벤치 마크는 사용 사례마다 크게 다른 결과를 보여 주므로 STL이 수정하도록 설계된 정확한 종류의 문제입니다.
복사 비용을 부과하지 않고 가능한 경우 Qt 컨테이너를 사용하지 말고 가능하면 랩퍼 또는 새 구문을 통해 STL 유형 반복을 사용하십시오.
Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
Qt의 Java 스타일 반복자는 C ++ 11에 추가되지 않았습니다. 그들은 그것을 앞선다. 어쨌든 Qt foreach(QString elem, list)
는 C ++ 11의 foreach 또는 BOOST_FOREACH처럼 쉽고 C ++ 11 이전 호환 컴파일러와 함께 작동합니다.
So, as we can see, this interface gains us nothing except an additional interface, *on top of* an already sleek, streamlined, and modern interface. Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
(강조 광산) 당신은 우리에게 foreach의 C ++ 11과 BOOST 버전을 보여 주 자마자 Qt 버전이 AFAICT가 아닌 두 가지 중 하나에서 만들어진 것처럼 들리도록 말했습니다. 나는 그것이 당신이 의도 한 것이 아니라고 확신하지만 그것이 그렇게되는 방식입니다. 따라서 "오해의 소지가있는 정보".
It's an additional layer of abstraction (and an unnecessary one) that bloats the interface, which is not easier.
당신이 비교하는 내용이 여전히 명확하지 않습니다. C ++ 03 이터레이터? C ++ 11 반복자? BOOST_FOREACH? 무엇보다도?
STL 컨테이너 :
Qt 컨테이너는 COW (Copy On Write) 관용구를 사용합니다.
std::basic_string
표준이며 C ++ 11로 조치를 취해이를 부적합하게 만들었습니다.
주요 문제 중 하나는 Qt의 API가 Qt의 컨테이너에 데이터를 제공 할 것을 기대한다는 것입니다. 따라서 Qt 컨테이너를 앞뒤로 변환하지 않고 간단히 Qt 컨테이너를 사용할 수도 있습니다.
또한 이미 Qt 컨테이너를 사용하고 있다면 STL 헤더 파일을 포함하지 않고 STL 라이브러리에 링크 할 필요가 없으므로 독점적으로 사용하는 것이 약간 더 좋습니다. 그러나 툴체인에 따라 어쨌든 발생할 수 있습니다. 순전히 디자인 관점에서 일관성은 일반적으로 좋은 것입니다.
COW 차이 외에도 STL 컨테이너는 다양한 플랫폼에서 훨씬 더 광범위하게 지원됩니다. Qt는 작업을 "주류"플랫폼으로 제한하는 경우 이식성이 충분하지만 STL은 다른 많은 모호한 플랫폼 (예 : Texas Instruments의 DSP)에서도 사용할 수 있습니다.
STL은 단일 회사가 제어하는 것이 아니라 표준이기 때문에 일반적으로 STL 코드를 쉽게 읽고 이해하고 수정할 수있는 더 많은 프로그래머와 더 많은 리소스 (책, 온라인 포럼, 회의 등)를 지원하는 프로그래머가 있습니다. Qt보다 있습니다. 그렇기 때문에 Qt에서 부끄러워해야한다는 것은 아닙니다. 단지 다른 모든 것들이 동일하다면 STL을 기본값으로 설정해야하지만 물론 모든 것이 거의 동일하지 않으므로 자신의 상황에서 가장 적합한 것을 결정해야합니다.
AlexKR의 답변과 관련하여 : STL 성능은 한계 내에서 보장되지만, 주어진 구현은 플랫폼 종속적 세부 사항을 사용 하여 STL 속도 를 높일 수 있습니다. 따라서 이러한 의미에서 플랫폼마다 다른 결과를 얻을 수 있지만 명시 적 보증 (모듈로 버그)보다 느리게 진행되지는 않습니다.
내 5 센트 : Qt 컨테이너는 다른 플랫폼에서 비슷하게 작동해야합니다. STL 컨테이너는 STL 구현에 의존합니다. 다른 성능 결과가 나타날 수 있습니다.
편집 :
STL이 "느리다"고 말하지는 않지만 다양한 구현 세부 사항의 영향을 지적합니다.
이것을
확인한 다음 어쩌면 이 .
그리고 그것은 STL의 실제 문제가 아닙니다. 분명히 성능에 큰 차이가 있다면 STL을 사용하는 코드에 문제가있는 것입니다.
Qt를 사용하는 방식에 달려 있다고 생각합니다. 제품 전체에서 사용하는 경우 Qt 컨테이너를 사용하는 것이 합리적입니다. UI 부분에만 포함 된 경우 C ++ 표준 컨테이너를 사용하는 것이 좋습니다.
QVector에는 (때로는) 큰 제한이 있습니다. int 바이트의 메모리 만 할당 할 수 있습니다 (한도는 요소 수가 아닌 바이트 단위 임). 이는 QVector로 ~ 2GB보다 큰 연속 메모리 블록을 할당하려고하면 충돌이 발생한다는 것을 의미합니다. 이것은 Qt 4와 5에서 발생합니다. std :: vector에는 이러한 제한이 없습니다.
STL 컨테이너를 사용해야하는 주된 이유는 매우 큰 컨테이너에서 메모리를 재사용하기 위해 사용자 지정 할당자가 필요한 경우입니다. 예를 들어 1000000 개의 항목 (키 / 값 쌍)을 저장하는 QMap이 있다고 가정하십시오. Qt에서는 정확히 1000000 백만 할당 ( new
호출) 을 의미합니다 . STL에서는 항상 모든 메모리를 한 번에 내부적으로 할당하고 맵이 채워질 때 각 항목에 할당하는 사용자 지정 할당자를 만들 수 있습니다.
필자는 비즈니스 로직에서 성능 결정 알고리즘을 작성할 때 STL 컨테이너를 사용하고 결과가 필요할 경우 UI 컨트롤 및 폼에 의해 표시 될 준비가되면이를 Qt 컨테이너로 다시 변환하는 것이 좋습니다.
QMapNode<K,V>
당신을 위해 K
, V
당신의 자신의를 제공하기 위해 operator new
.