Tôi thấy một số câu trả lời hợp lệ ở đây, nhưng tôi sẽ tìm hiểu thêm một chút về chi tiết.
Chuyển đến phần tóm tắt dưới đây để biết câu trả lời cho câu hỏi chính của bạn nếu bạn không muốn đi qua toàn bộ bức tường văn bản này.
Trừu tượng
Vì vậy, trong trường hợp này, tôi đang trả tiền cho cái gì?
Bạn đang trả tiền cho sự trừu tượng . Có thể viết mã đơn giản và thân thiện hơn với con người phải trả giá. Trong C ++, một ngôn ngữ hướng đối tượng, hầu hết mọi thứ đều là đối tượng. Khi bạn sử dụng bất kỳ đối tượng nào, ba điều chính sẽ luôn xảy ra dưới mui xe:
- Tạo đối tượng, về cơ bản cấp phát bộ nhớ cho chính đối tượng và dữ liệu của nó.
- Khởi tạo đối tượng (thường thông qua một số
init()
phương thức). Thông thường phân bổ bộ nhớ xảy ra dưới mui xe là điều đầu tiên trong bước này.
- Phá hủy đối tượng (không phải luôn luôn).
Bạn không nhìn thấy nó trong mã, nhưng mỗi lần bạn sử dụng một đối tượng, cả ba điều trên đều cần phải xảy ra bằng cách nào đó. Nếu bạn làm mọi thứ bằng tay, mã rõ ràng sẽ dài hơn.
Bây giờ, việc trừu tượng hóa có thể được thực hiện một cách hiệu quả mà không cần thêm chi phí: phương pháp nội tuyến và các kỹ thuật khác có thể được sử dụng bởi cả trình biên dịch và lập trình viên để loại bỏ các chi phí trừu tượng, nhưng đây không phải là trường hợp của bạn.
Điều gì thực sự xảy ra trong C ++?
Đây rồi, chia nhỏ:
- Các
std::ios_base
lớp được khởi tạo, đó là lớp cơ sở cho tất cả mọi thứ I / O có liên quan.
- Đối
std::cout
tượng được khởi tạo.
- Chuỗi của bạn được tải và chuyển đến
std::__ostream_insert
, mà (như bạn đã tìm ra bằng tên) là một phương thức std::cout
(về cơ bản là <<
toán tử) để thêm một chuỗi vào luồng.
cout::endl
cũng được truyền cho std::__ostream_insert
.
__std_dso_handle
được chuyển đến __cxa_atexit
, đây là chức năng toàn cầu chịu trách nhiệm "dọn dẹp" trước khi thoát khỏi chương trình. __std_dso_handle
chính nó được gọi bởi chức năng này để phân bổ và tiêu diệt các đối tượng toàn cầu còn lại.
Vậy sử dụng C == không trả tiền cho bất cứ điều gì?
Trong mã C, rất ít bước đang diễn ra:
- Chuỗi của bạn được tải và chuyển
puts
qua thông qua thanh edi
ghi.
puts
được gọi.
Không có đối tượng ở bất cứ đâu, do đó không cần phải khởi tạo / phá hủy bất cứ thứ gì.
Tuy nhiên điều này không có nghĩa là bạn không phải "trả tiền" cho bất cứ điều gì trong C . Bạn vẫn đang trả tiền cho sự trừu tượng hóa, đồng thời khởi tạo thư viện chuẩn C và độ phân giải động của printf
hàm (hoặc, thực tế puts
, được trình biên dịch tối ưu hóa do bạn không cần bất kỳ chuỗi định dạng nào) vẫn xảy ra trong trình duyệt.
Nếu bạn viết chương trình này trong tập hợp thuần túy, nó sẽ trông giống như thế này:
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
Mà về cơ bản chỉ dẫn cách gọi các write
syscall tiếp theo là exit
syscall. Bây giờ đây sẽ là mức tối thiểu để hoàn thành điều tương tự.
Tóm tắt
C là cách đơn giản hơn và chỉ thực hiện mức tối thiểu cần thiết, để lại toàn quyền kiểm soát cho người dùng, có thể tối ưu hóa hoàn toàn và tùy chỉnh về cơ bản mọi thứ họ muốn. Bạn bảo bộ xử lý tải một chuỗi trong một thanh ghi và sau đó gọi hàm thư viện để sử dụng chuỗi đó. Mặt khác, C ++ phức tạp và trừu tượng hơn nhiều . Điều này có lợi thế rất lớn khi viết mã phức tạp, và cho phép dễ viết hơn và mã thân thiện với con người hơn, nhưng rõ ràng nó có chi phí. Luôn luôn có một nhược điểm về hiệu suất trong C ++ nếu so với C trong các trường hợp như thế này, vì C ++ cung cấp nhiều hơn những gì cần thiết để hoàn thành các nhiệm vụ cơ bản như vậy, và do đó nó tăng thêm chi phí .
Trả lời câu hỏi chính của bạn :
Tôi đang trả tiền cho những gì tôi không ăn?
Trong trường hợp cụ thể này, có . Bạn không tận dụng bất cứ thứ gì mà C ++ phải cung cấp nhiều hơn C, nhưng đó chỉ là vì không có gì trong đoạn mã đơn giản mà C ++ có thể giúp bạn: thật đơn giản đến nỗi bạn thực sự không cần C ++.
Ồ, và chỉ một điều nữa!
Những ưu điểm của C ++ có thể trông không rõ ràng ngay từ cái nhìn đầu tiên, vì bạn đã viết một chương trình rất đơn giản và nhỏ, nhưng nhìn vào một ví dụ phức tạp hơn một chút và thấy sự khác biệt (cả hai chương trình đều thực hiện cùng một điều chính xá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;
}
Hy vọng bạn có thể thấy rõ những gì tôi muốn nói ở đây. Cũng lưu ý rằng trong C bạn phải quản lý bộ nhớ ở mức thấp hơn bằng cách sử dụng malloc
và free
cách bạn cần cẩn thận hơn về lập chỉ mục và kích thước, và cách bạn cần phải rất cụ thể khi lấy đầu vào và in.