C ++에서 내가 먹지 않은 것에 대해 지불하고 있습니까?
C 및 C ++의 다음 hello world 예제를 고려하십시오.
#include <stdio.h>
int main()
{
printf("Hello world\n");
return 0;
}
#include <iostream>
int main()
{
std::cout<<"Hello world"<<std::endl;
return 0;
}
Godbolt에서 어셈블리로 컴파일 할 때 C 코드의 크기는 9 줄 ( gcc -O3
)입니다.
.LC0:
.string "Hello world"
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
call puts
xor eax, eax
add rsp, 8
ret
그러나 C ++ 코드의 크기는 22 줄 ( g++ -O3
)입니다.
.LC0:
.string "Hello world"
main:
sub rsp, 8
mov edx, 11
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
xor eax, eax
add rsp, 8
ret
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
add rsp, 8
jmp __cxa_atexit
... 훨씬 큽니다.
C ++에서는 먹는 것을 지불하는 것이 유명합니다. 그래서이 경우, 나는 무엇을 지불하고 있습니까?
당신이 지불하는 것은 무거운 라이브러리 (콘솔에 인쇄하는 것만 큼 무겁지 않은)를 호출하는 것입니다. ostream
객체를 초기화 합니다. 숨겨진 스토리지가 있습니다. 그런 다음 std::endl
의 동의어가 아닌을 ( 를) 호출 합니다 \n
. iostream
라이브러리는 여러 설정을 조정하고 프로그래머가 아닌 프로세서에 부담을 넣어하는 데 도움이됩니다. 이것이 당신이 지불하는 것입니다.
코드를 검토하자 :
.LC0:
.string "Hello world"
main:
ostream 객체 + cout 초기화
sub rsp, 8
mov edx, 11
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
cout
새 줄을 인쇄하고 플러시하기 위해 다시 전화
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
xor eax, eax
add rsp, 8
ret
정적 스토리지 초기화 :
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
add rsp, 8
jmp __cxa_atexit
또한 언어와 라이브러리를 구별하는 것이 필수적입니다.
BTW, 이것은 이야기의 일부일뿐입니다. 호출하는 함수에 쓰여진 내용을 모릅니다.
그래서이 경우, 나는 무엇을 지불하고 있습니까?
std::cout
보다 강력하고 복잡합니다 printf
. 로케일, 상태 저장 형식 플래그 등을 지원합니다.
당신은 그 사용이 필요하지 않은 경우 std::printf
또는 std::puts
- 그들에게있는 거 로모을 <cstdio>
.
C ++에서는 먹는 것을 지불하는 것이 유명합니다.
또한 C ++ ! = C ++ 표준 라이브러리 임을 분명히하고 싶습니다 . 표준 라이브러리는 범용이며 "충분히 빠르다"고 가정하지만 필요한 것의 특수 구현보다 속도가 느릴 수 있습니다.
반면에 C ++ 언어는 불필요한 추가 비용을 지불하지 않고 코드를 작성할 수 있도록 노력합니다 (예 : 옵트 인 virtual
, 가비지 수집 없음).
C와 C ++을 비교하지 않습니다. 당신은 비교할 printf
와 std::cout
다른 일 (로케일, 상태 서식, 등) 할 수있는,.
다음 코드를 사용하여 비교해보십시오. Godbolt는 두 파일 모두에 대해 동일한 어셈블리를 생성합니다 (gcc 8.2, -O3으로 테스트).
main.c :
#include <stdio.h>
int main()
{
int arr[6] = {1, 2, 3, 4, 5, 6};
for (int i = 0; i < 6; ++i)
{
printf("%d\n", arr[i]);
}
return 0;
}
main.cpp :
#include <array>
#include <cstdio>
int main()
{
std::array<int, 6> arr {1, 2, 3, 4, 5, 6};
for (auto x : arr)
{
std::printf("%d\n", x);
}
}
귀하의 목록은 실제로 사과와 오렌지를 비교하지만 대부분의 다른 답변에 암시되는 것은 아닙니다.
코드의 실제 기능을 확인하십시오.
씨:
- 단일 문자열을 인쇄
"Hello world\n"
C ++ :
- 문자열
"Hello world"
을std::cout
std::endl
조작기를 스트림std::cout
분명히 C ++ 코드는 두 배나 많은 작업을 수행하고 있습니다. 공정한 비교를 위해 다음을 결합해야합니다.
#include <iostream>
int main()
{
std::cout<<"Hello world\n";
return 0;
}
… 갑자기 어셈블리 코드는 main
C와 매우 유사합니다.
main:
sub rsp, 8
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
xor eax, eax
add rsp, 8
ret
실제로 C와 C ++ 코드를 한 줄씩 비교할 수 있으며 차이점 은 거의 없습니다 .
sub rsp, 8 sub rsp, 8
mov edi, OFFSET FLAT:.LC0 | mov esi, OFFSET FLAT:.LC0
> mov edi, OFFSET FLAT:_ZSt4cout
call puts | call std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
xor eax, eax xor eax, eax
add rsp, 8 add rsp, 8
ret ret
유일한 차이점은 C ++ operator <<
에서 두 개의 인수 ( std::cout
및 문자열)로 호출한다는 것입니다 . 더 가까운 C eqivalent :를 사용하면 약간의 차이도 제거 할 수 fprintf
있는데,이 스트림에도 스트림을 지정하는 첫 번째 인수가 있습니다.
이것에 대한 어셈블리 코드 _GLOBAL__sub_I_main
는 C ++에서는 생성되지만 C에서는 생성되지 않습니다. 이것은이 어셈블리 목록에서 볼 수있는 유일한 실제 오버 헤드입니다 ( 물론 두 언어에 대해 더 많은 보이지 않는 오버 헤드 가 있습니다). 이 코드는 C ++ 프로그램 시작시 일부 C ++ 표준 라이브러리 함수의 일회성 설정을 수행합니다.
그러나 다른 답변에서 설명 했듯이이 두 프로그램 간의 관련 차이점은 main
모든 무거운 리프팅이 장면 뒤에서 발생하기 때문에 함수 의 어셈블리 출력에서 찾을 수 없습니다 .
C ++에서는 먹는 것을 지불하는 것이 유명합니다. 그래서이 경우, 나는 무엇을 지불하고 있습니까?
간단합니다. 당신은 지불합니다 std::cout
. "먹는 것만 지불한다"는 것이 "항상 가장 좋은 가격을 받는다"는 의미는 아닙니다. 물론 printf
더 저렴합니다. std::cout
더 안전하고 다재다능하다고 주장 할 수 있으므로 더 큰 비용이 정당화되지만 (비용이 많이 들지만 더 많은 가치를 제공함), 요점을 놓치게됩니다. 당신은 사용하지 않는 printf
당신은 사용, std::cout
사용 비용을 지불하므로 std::cout
. 을 (를) 사용하여 비용을 지불하지 않습니다 printf
.
좋은 예는 가상 함수입니다. 가상 함수에는 런타임 비용과 공간 요구 사항이 있지만 실제로 사용하는 경우에만 필요 합니다. 가상 기능을 사용하지 않으면 아무것도 지불하지 않습니다.
몇 가지 언급
C ++ 코드가 더 많은 어셈블리 명령어로 평가 되더라도 여전히 소수의 명령어이며 실제 I / O 작업으로 인해 성능 오버 헤드가 여전히 줄어 듭니다.
실제로, 때로는 "C ++에서는 먹는 것에 대해 지불하는 것"보다 낫습니다. 예를 들어, 컴파일러는 일부 상황에서 가상 함수 호출이 필요하지 않다고 추론하고이를 가상이 아닌 호출로 변환 할 수 있습니다. 그것은 당신이 무료로 가상 기능을 얻을 수 있음을 의미합니다 . 대단하지 않습니까?
"printf에 대한 어셈블리리스트"는 printf에 대한 것이 아니라 puts에 대한 것입니다 (컴파일러 최적화의 종류?); printf는 예전보다 훨씬 복잡합니다 ... 잊지 마세요!
여기에 유효한 답변이 있지만 세부 사항에 대해 조금 더 설명하겠습니다.
이 전체 텍스트를 통과하고 싶지 않은 경우 주요 질문에 대한 답변을 보려면 아래 요약으로 이동하십시오.
추출
그래서이 경우, 나는 무엇을 지불하고 있습니까?
추상화 비용을 지불하고 있습니다. 더 간단하고 인간 친화적 인 코드를 작성할 수있는 비용이 발생합니다. 객체 지향 언어 인 C ++에서 거의 모든 것이 객체입니다. 어떤 물체를 사용하면 항상 세 가지 주요 일이 발생합니다.
- 객체 생성, 기본적으로 객체 자체와 데이터에 대한 메모리 할당.
- 객체 초기화 (보통 일부
init()
방법을 통해 ). 일반적으로 메모리 할당은이 단계에서 가장 먼저 발생합니다. - 객체 파괴 (항상 그런 것은 아님).
코드에서 볼 수는 없지만 매번 객체를 사용할 때마다 위의 세 가지 일이 모두 발생해야합니다. 모든 것을 수동으로 수행한다면 코드는 분명히 길어질 것입니다.
이제 오버 헤드를 추가하지 않고도 추상화를 효율적으로 수행 할 수 있습니다. 컴파일러와 프로그래머 모두 메소드 인라이닝 및 기타 기술을 사용하여 추상화의 오버 헤드를 제거 할 수 있지만, 그렇지 않습니다.
C ++에서 실제로 무슨 일이 일어나고 있습니까?
여기에 세분화되어 있습니다.
std::ios_base
클래스는 모든 것을 I / 관련 O의 기본 클래스 인, 초기화됩니다.std::cout
개체가 초기화됩니다.- 문자열은에로드되어 전달됩니다
std::__ostream_insert
. (이미 이름으로 알 수 있듯이 ) 문자열을 스트림에 추가하는 방법std::cout
(기본적으로<<
연산자)입니다. cout::endl
또한로 전달됩니다std::__ostream_insert
.__std_dso_handle
는__cxa_atexit
프로그램으로 나가기 전에 "세척"을 담당하는 전역 함수 인 로 전달됩니다 .__std_dso_handle
이 함수는 나머지 전역 객체를 할당 해제하고 파괴하기 위해 자체적으로 호출됩니다.
C ==를 사용하여 아무것도 지불하지 않습니까?
C 코드에서 매우 적은 단계가 발생합니다.
- 문자열이로드되고 레지스터 를
puts
통해 전달됩니다edi
. puts
호출됩니다.
어디에도 객체가 없으므로 초기화 / 파괴 할 필요가 없습니다.
그러나 이것이 C의 어떤 것에 대해서도 "지불"하지 않는다는 것을 의미하지는 않습니다 . 여전히 추상화 비용을 지불하고 있으며 C 표준 라이브러리의 초기화 및 printf
함수 (또는 실제로 puts
는 형식 문자열이 필요하지 않으므로 컴파일러에서 최적화 된)의 동적 해상도를 여전히 사용하고 있습니다.
이 프로그램을 순수한 어셈블리로 작성한다면 다음과 같이 보일 것입니다.
jmp start
msg db "Hello world\n"
start:
mov rdi, 1
mov rsi, offset msg
mov rdx, 11
mov rax, 1 ; write
syscall
xor rdi, rdi
mov rax, 60 ; exit
syscall
어떤 기본적에만 호출 결과 write
콜 에 의해 다음 exit
콜을. 이제 이것은 동일한 것을 달성하기위한 최소한의 것입니다.
요약
C는 더 베어 본 이며 필요한 최소한의 값만 사용하면 사용자가 모든 것을 완벽하게 제어 할 수 있으므로 원하는 것을 기본적으로 완전히 최적화하고 사용자 지정할 수 있습니다. 프로세서에 레지스터에 문자열을로드 한 다음 라이브러리 함수를 호출하여 해당 문자열을 사용하도록 지시합니다. 반면에 C ++은 더 복잡하고 추상적 입니다. 이것은 복잡한 코드를 작성할 때 엄청난 이점이 있으며, 작성하기 쉽고 인간 친화적 인 코드를 허용하지만 비용이 많이 듭니다. C ++이 이러한 기본 작업을 수행하는 데 필요한 것 이상을 제공하므로 오버 헤드가 더 많기 때문에 C 와 비교할 경우 항상 C ++에서 성능이 저하 될 수 있습니다 .
주요 질문에 대한 답변 :
내가 먹지 않는 것에 대해 비용을 지불하고 있습니까?
이 특정한 경우에는 yes 입니다. C ++이 C보다 더 많은 것을 제공하는 것을 활용하지는 않지만, C ++이 도와 줄 수있는 간단한 코드에는 아무것도 없기 때문입니다. 너무 간단해서 C ++이 전혀 필요하지 않습니다.
아, 한가지 만 더!
매우 간단하고 작은 프로그램을 작성했지만 조금 더 복잡한 예제를보고 차이점을 확인하기 때문에 C ++의 장점은 언뜻보기에 분명하지 않을 수 있습니다.
C :
#include <stdio.h>
#include <stdlib.h>
int cmp(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
int main(void) {
int i, n, *arr;
printf("How many integers do you want to input? ");
scanf("%d", &n);
arr = malloc(sizeof(int) * n);
for (i = 0; i < n; i++) {
printf("Index %d: ", i);
scanf("%d", &arr[i]);
}
qsort(arr, n, sizeof(int), cmp)
puts("Here are your numbers, ordered:");
for (i = 0; i < n; i++)
printf("%d\n", arr[i]);
free(arr);
return 0;
}
C ++ :
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(void) {
int n;
cout << "How many integers do you want to input? ";
cin >> n;
vector<int> vec(n);
for (int i = 0; i < vec.size(); i++) {
cout << "Index " << i << ": ";
cin >> vec[i];
}
sort(vec.begin(), vec.end());
cout << "Here are your numbers:" << endl;
for (int item : vec)
cout << item << endl;
return 0;
}
바라건대 여기서 내가 의미하는 바를 명확하게 볼 수 있습니다. 사용 낮은 수준의 메모리를 관리 할 수있는 방법 C에서 또한 통보 malloc
및 free
인덱싱 및 크기에 대한 더 조심해야하는지, 그리고 입력을 복용하고 인쇄 할 때 매우 구체적으로해야하는지.
시작할 때 몇 가지 오해가 있습니다. 첫째, C ++ 프로그램 은 22 개의 명령어를 생성 하지 않으며 , 22,000 개와 비슷합니다 (저는 모자에서 그 숫자를 가져 왔지만 대략 야구장에 있습니다). 또한 C 코드 는 9 개의 명령어를 생성 하지 않습니다 . 그것들은 당신이 보는 것입니다.
C 코드는 보이지 않는 많은 작업을 수행 한 후 CRT (보통 공유 라이브러리로 존재하지는 않지만)에서 함수를 호출 한 다음 반환 값을 확인하거나 처리 하지 않습니다. 오류와 구제. 컴파일러와 최적화 설정에 따라 실제로 호출 printf
하지는 않지만 puts
훨씬 원시적입니다.
동일한 함수를 같은 방식으로 호출 한 경우 C ++에서 동일한 프로그램 (보이지 않는 초기화 함수 제외)을 작성했을 수도 있습니다. 또는 수퍼 교정을 원한다면 동일한 함수 앞에 접두사가 붙습니다 std::
.
해당 C ++ 코드는 실제로는 전혀 동일하지 않습니다. <iostream>
그것 의 전체가 작은 프로그램에 대한 엄청난 오버 헤드를 추가하는 뚱뚱한 못생긴 돼지로 잘 알려져 있지만 ( "실제"프로그램에서는 그다지 눈치 채지 못합니다) 다소 공정한 해석은 그것이 끔찍한 일이라는 것입니다 당신이 보지 못하고 작동 하는 많은 것들 . 다른 숫자 형식과 로케일, 기타 등등, 버퍼링 및 적절한 오류 처리를 포함하여 거의 모든 우발적 인 물건의 마법 형식을 포함하지만 이에 국한되지 않습니다. 오류 처리? 글쎄, 문자열 출력이 실제로 실패 할 수있는 것을 추측하십시오 .C 프로그램과 달리 C ++ 프로그램은 이것을 자동으로 무시 하지 않습니다 . 무엇을 고려std::ostream
사람들이 알아 차리지 않고 실제로는 매우 가볍습니다. 나는 열정으로 스트림 구문을 싫어하기 때문에 그것을 사용하는 것과 다릅니다. 그러나 여전히 당신이하는 일을 고려하면 꽤 굉장합니다.
그러나 C ++ 전체는 C만큼 효율적 이지 않습니다 . 이 같은 일이 아니며이되지 않기 때문에 그것은 효율적으로 할 수없는 일을 같은 일을. 다른 것이 없으면 C ++은 예외를 생성하고 (생성, 처리 또는 실패하는 코드) C가 제공하지 않는다는 보장을합니다. 따라서 C ++ 프로그램은 반드시 조금 더 커야합니다. 그러나 큰 그림에서 이것은 중요하지 않습니다. 반대로 실제 프로그램의 경우 C ++이 어떤 이유로 든 더 나은 성능을 제공하는 것으로 보지 못했습니다. 왜 그런지 묻지 마라.
최선을 다하는 것 대신에 C 코드를 작성하는 것이 올바른 경우 (즉, 실제로 오류를 확인하고 오류가있는 경우 프로그램이 올바르게 작동하는 경우) 차이는 미미합니다. 존재하는 경우.
당신은 실수를 지불하고 있습니다. 80 년대 컴파일러가 형식 문자열을 확인하기에 충분하지 않은 경우 연산자 오버로드는 io 중 유형 안전과 유사한 형태를 시행하는 좋은 방법으로 간주되었습니다. 그러나 모든 배너 기능은 처음부터 잘못되거나 개념적으로 파산되었습니다.
<iomanip>
C ++ 스트림 IO API의 가장 유명한 부분은이 형식화 헤더 라이브러리가 존재한다는 것입니다. 상태가 좋고 추악하고 오류가 발생하기 쉬우 며 서식을 스트림에 연결합니다.
8 자리의 0으로 채워진 16 진 부호없는 int와 공백, 소수점 이하 3 자리를 갖는 행을 인쇄한다고 가정하십시오. 을 사용하면 <cstdio>
간결한 형식 문자열을 읽을 수 있습니다. 을 사용하면 <ostream>
이전 상태를 저장하고 정렬을 오른쪽으로 설정하고 채우기 문자를 설정하고 채우기 너비를 설정하고 밑을 16 진수로 설정하고 정수를 출력하고 저장된 상태를 복원하십시오 (그렇지 않으면 정수 형식은 부동 형식을 오염시킵니다) , 표기법을 고정으로 설정하고 정밀도를 설정하고 이중 및 개행을 출력 한 다음 이전 형식을 복원하십시오.
// <cstdio>
std::printf( "%08x %.3lf\n", ival, fval );
// <ostream> & <iomanip>
std::ios old_fmt {nullptr};
old_fmt.copyfmt (std::cout);
std::cout << std::right << std::setfill('0') << std::setw(8) << std::hex << ival;
std::cout.copyfmt (old_fmt);
std::cout << " " << std::fixed << std::setprecision(3) << fval << "\n";
std::cout.copyfmt (old_fmt);
연산자 오버로딩
<iostream>
연산자 오버로드를 사용하지 않는 방법의 포스터 자식입니다.
std::cout << 2 << 3 && 0 << 5;
공연
std::cout
몇 배 느립니다 printf()
. 만연한 기능 장애와 가상 파견은 큰 타격을받습니다.
스레드 안전
모두 <cstdio>
와 <iostream>
모든 함수 호출 원자 인 것을 스레드 안전합니다. 그러나 printf()
통화 당 더 많은 작업을 수행 할 수 있습니다. <cstdio>
옵션으로 다음 프로그램을 실행하면 의 행만 표시됩니다 f
. <iostream>
멀티 코어 머신에서 사용 하는 경우 다른 것을 볼 수 있습니다.
// g++ -Wall -Wextra -Wpedantic -pthread -std=c++17 cout.test.cpp
#define USE_STREAM 1
#define REPS 50
#define THREADS 10
#include <thread>
#include <vector>
#if USE_STREAM
#include <iostream>
#else
#include <cstdio>
#endif
void task()
{
for ( int i = 0; i < REPS; ++i )
#if USE_STREAM
std::cout << std::hex << 15 << std::dec;
#else
std::printf ( "%x", 15);
#endif
}
int main()
{
auto threads = std::vector<std::thread> {};
for ( int i = 0; i < THREADS; ++i )
threads.emplace_back(task);
for ( auto & t : threads )
t.join();
#if USE_STREAM
std::cout << "\n<iostream>\n";
#else
std::printf ( "\n<cstdio>\n" );
#endif
}
이 예제의 레토르트는 대부분의 사람들이 여러 스레드에서 단일 파일 디스크립터에 쓰지 않기 위해 훈련을 받는다는 것입니다. 음, 그 경우에, 당신은 관찰해야합니다 <iostream>
유용하게 모든에 대한 잠금 사로 잡고 <<
모든를 >>
. 에 있지만 <cstdio>
자주 잠금을 해제하지 않으며 잠금을 해제하는 옵션도 있습니다.
<iostream>
덜 일관된 결과를 얻기 위해 더 많은 잠금을 소비합니다.
모든 다른 답변이 말한뿐만 아니라,
또한 사실 거기 std::endl
입니다 하지 같은 '\n'
.
불행히도 일반적인 오해입니다. std::endl
"새 줄"을 의미하는 것이 아니라 "새 줄을
인쇄 한 다음 스트림을 플러시 "한다는 의미 입니다. 플러싱은 싸지 않습니다!
완전히 사이의 차이를 무시 printf
하고 std::cout
잠시를 기능적으로 귀하의 C 예제에 eqvuialent 할하려면 C ++ 예는 다음과 같습니다한다고 :
#include <iostream>
int main()
{
std::cout << "Hello world\n";
return 0;
}
플러싱을 포함하는 경우 예제의 예는 다음과 같습니다.
씨
#include <stdio.h>
int main()
{
printf("Hello world\n");
fflush(stdout);
return 0;
}
C ++
#include <iostream>
int main()
{
std::cout << "Hello world\n";
std::cout << std::flush;
return 0;
}
코드를 비교할 때는 항상 비교를 좋아 하고 코드가 수행하는 작업의 의미를 이해해야합니다. 때로는 가장 간단한 예조차도 사람들이 생각하는 것보다 더 복잡합니다.
기존 기술 답변은 정확하지만이 질문은 궁극적으로이 오해에서 비롯된 것으로 생각합니다.
C ++에서는 먹는 것을 지불하는 것이 유명합니다.
이것은 C ++ 커뮤니티의 마케팅 강연입니다. (공평하게 말하면, 모든 언어 커뮤니티에서 마케팅 대화가 있습니다.) 이것은 당신이 진지하게 의지 할 수있는 구체적인 것을 의미하지는 않습니다.
"사용하는 대금을 지불"한다는 것은 C ++ 기능이 해당 기능을 사용하는 경우 오버 헤드 만 있음을 의미합니다. 그러나 "특징"의 정의는 무한정 세분화되지 않습니다. 여러 측면이있는 기능을 활성화하는 경우가 종종 있으며 이러한 측면의 하위 집합 만 필요한 경우에도 구현에서 기능을 부분적으로 가져 오는 것은 실용적이지 않거나 불가능합니다.
일반적으로, 많은 언어 (다만 모든 언어는 아님)는 다양한 수준의 성공을 거두면서 효율적으로 노력합니다. C ++은 규모가 어딘가에 있지만이 목표에서 완벽하게 성공할 수있는 디자인에는 특별한 또는 마술이 없습니다.
C ++의 입력 / 출력 기능은 우아하게 작성되었으며 사용하기 쉽도록 설계되었습니다. 여러 측면에서 C ++의 객체 지향 기능에 대한 쇼케이스입니다.
그러나 실제로 대가로 약간의 성능을 포기하지만 운영 체제가 기능을 낮은 수준에서 처리하는 데 걸리는 시간과 비교하면 무시할 만합니다.
C 스타일 함수는 C ++ 표준의 일부이므로 항상 C 스타일 함수로 넘어가거나 이식성을 완전히 포기하고 운영 체제에 대한 직접 호출을 사용할 수 있습니다.
다른 답변에서 볼 수 있듯이 일반 라이브러리에서 링크하고 복잡한 생성자를 호출하면 비용을 지불합니다. 여기에 특별한 질문은 없습니다. 나는 실제 측면을 지적 할 것이다 :
Barne은 효율성이 C ++이 아닌 C에 머무르는 이유가되지 않도록하는 핵심 설계 원칙을 가지고있었습니다. 즉, 이러한 효율성을 확보하기 위해주의를 기울여야하며, 항상 효과가 있지만 C 사양 내에서 '기술적으로'그렇지 않은 효율성이 가끔 있습니다. 예를 들어, 비트 필드의 레이아웃은 실제로 지정되지 않았습니다.
ostream을 통해보십시오. 맙소사! 나는 거기에서 비행 시뮬레이터를 발견하는 것에 놀라지 않을 것입니다. stdlib의 printf ()조차도 약 50K를 실행합니다. 이것들은 게으른 프로그래머가 아닙니다. printf 크기의 절반은 대부분의 사람들이 사용하지 않는 간접적 인 정밀 인수와 관련이 있습니다. 거의 모든 제약이있는 프로세서 라이브러리는 printf 대신 자체 출력 코드를 만듭니다.
크기가 커지면 일반적으로 더 유연하고 유연한 경험이 제공됩니다. 유사하게, 자동 판매기는 몇 동전에 커피와 같은 물질의 컵을 판매하며 전체 거래는 1 분이 걸립니다. 좋은 식당에 들어가려면 테이블 설정, 앉기, 주문, 대기, 멋진 컵 받기, 청구서 받기, 양식 선택, 팁 추가, 퇴근길 좋은 날 등이 포함됩니다. 그것은 다른 경험이며, 복잡한 식사를 위해 친구들과 함께 들어가면 더 편리합니다.
K & R C는 거의 없지만 사람들은 여전히 ANSI C를 작성합니다. 필자의 경험에 따르면 항상 몇 가지 구성 조정을 사용하여 C ++ 컴파일러로 컴파일하여 드래그되는 것을 제한합니다. 다른 언어에는 좋은 인수가 있습니다. Go는 다형성 오버 헤드와 미친 전처리기를 제거합니다. ; 더 스마트 한 필드 패킹 및 메모리 레이아웃에 대한 좋은 주장이있었습니다. IMHO 저는 모든 언어 디자인이 Python of Zen 과 같은 목표 목록으로 시작해야한다고 생각합니다 .
재미있는 토론이었습니다. 당신은 왜 마술처럼 작고, 단순하고, 우아하고, 완전하고 유연한 라이브러리를 가질 수 없는지 묻습니다.
답이 없습니다. 답변이 없습니다. 그게 답입니다.
참고 URL : https://stackoverflow.com/questions/52442415/in-c-am-i-paying-for-what-i-am-not-eating
'development' 카테고리의 다른 글
ConstraintLayout을 백분율 값으로 작동시키는 방법은 무엇입니까? (0) | 2020.05.26 |
---|---|
바이트를 킬로바이트, 메가 바이트, 기가 바이트로 포맷 (0) | 2020.05.26 |
변경할 때 UITableView에 대한 reloadData에 애니메이션을 적용하십시오. (0) | 2020.05.26 |
C 또는 C ++에서 문자열을 어떻게 뒤집습니까? (0) | 2020.05.26 |
RxJava에서 언제 map vs flatMap을 사용합니까? (0) | 2020.05.25 |