Có phải thực tế xấu khi sử dụng trình biên dịch C ++ chỉ để nạp chồng hàm?
Quan điểm của IMHO, vâng, và tôi sẽ cần phải trở thành tâm thần phân liệt để trả lời câu hỏi này vì tôi yêu cả hai ngôn ngữ nhưng nó không liên quan gì đến hiệu quả, mà giống như sử dụng ngôn ngữ an toàn và thành ngữ.
Bên C
Từ quan điểm C, tôi thấy thật lãng phí khi làm cho mã của bạn yêu cầu C ++ chỉ để sử dụng chức năng nạp chồng. Trừ khi bạn đang sử dụng nó cho đa hình tĩnh với các mẫu C ++, thì đó là loại đường cú pháp tầm thường như vậy để đổi lấy việc chuyển sang một ngôn ngữ hoàn toàn khác. Hơn nữa nếu bạn muốn xuất các chức năng của mình sang một dylib (có thể hoặc không phải là mối quan tâm thực tế), bạn không còn có thể thực hiện điều đó một cách thực tế để tiêu thụ rộng rãi với tất cả các biểu tượng được xáo trộn.
Bên C ++
Từ quan điểm C ++, bạn không nên sử dụng C ++ như C với chức năng nạp chồng. Đây không phải là chủ nghĩa giáo điều cách điệu mà là một liên quan đến việc sử dụng thực tế C ++ hàng ngày.
Loại mã C thông thường của bạn chỉ hợp lý và "an toàn" để viết nếu bạn đang làm việc chống lại hệ thống loại C cấm những thứ như sao chép trong structs
. Khi bạn đang làm việc trong hệ thống loại phong phú hơn nhiều của C ++, các chức năng hàng ngày có giá trị rất lớn memset
và memcpy
không trở thành chức năng bạn nên dựa vào mọi lúc. Thay vào đó, chúng là các hàm mà bạn thường muốn tránh như bệnh dịch hạch, vì với các loại C ++, bạn không nên coi chúng như các bit và byte thô được sao chép và xáo trộn xung quanh và giải phóng. Ngay cả khi mã của bạn chỉ sử dụng những thứ như memset
trên nguyên thủy và POD UDT tại thời điểm này, thời điểm này, bất cứ ai cũng thêm một ctor vào bất kỳ UDT nào bạn sử dụng (bao gồm cả việc thêm một thành viên cần một, nhưstd::unique_ptr
thành viên) chống lại các chức năng đó hoặc một chức năng ảo hoặc bất cứ thứ gì thuộc loại đó, nó làm cho tất cả các mã hóa kiểu C thông thường của bạn dễ bị hành vi không xác định. Lấy nó từ Herb Sutter mình:
memcpy
và memcmp
vi phạm hệ thống loại. Sử dụng memcpy
để sao chép các đối tượng cũng giống như kiếm tiền bằng máy photocopy. Sử dụng memcmp
để so sánh các đối tượng cũng giống như so sánh báo đốm bằng cách đếm các điểm của chúng. Các công cụ và phương pháp có thể xuất hiện để thực hiện công việc, nhưng chúng quá thô để có thể chấp nhận nó. Các đối tượng C ++ là tất cả về việc che giấu thông tin (được cho là nguyên tắc có lợi nhất trong công nghệ phần mềm; xem Mục 11): Các đối tượng ẩn dữ liệu (xem Mục 41) và đưa ra các tóm tắt chính xác để sao chép dữ liệu đó thông qua các hàm tạo và toán tử gán (xem Mục 52 đến 55) . Việc xóa bỏ tất cả những gì memcpy
vi phạm nghiêm trọng về việc che giấu thông tin và thường dẫn đến rò rỉ bộ nhớ và tài nguyên (tốt nhất), sự cố (tệ hơn) hoặc hành vi không xác định (tệ nhất) - Tiêu chuẩn mã hóa C ++.
Vì vậy, nhiều nhà phát triển C sẽ không đồng ý với điều này và đúng như vậy, vì triết lý chỉ áp dụng nếu bạn đang viết mã bằng C ++. Bạn rất có thể được viết mã rất có vấn đề nếu bạn sử dụng các chức năng như memcpy
tất cả thời gian trong mã đó được xây dựng như C ++ , nhưng nó hoàn toàn tốt đẹp nếu bạn làm điều đó trong C . Hai ngôn ngữ rất khác nhau về vấn đề này vì sự khác biệt trong hệ thống loại. Thật là hấp dẫn khi nhìn vào tập hợp các tính năng mà hai tính năng này có điểm chung và tin rằng một cái có thể được sử dụng như cái kia, đặc biệt là về phía C ++, nhưng mã C + (hoặc mã C--) nói chung có vấn đề hơn nhiều so với cả C và Mã C ++.
Tương tự như vậy, bạn không nên sử dụng, malloc
trong bối cảnh kiểu C (ngụ ý không có EH) nếu nó có thể gọi bất kỳ hàm C ++ nào có thể ném trực tiếp, từ đó bạn có một điểm thoát ngầm trong hàm do kết quả của ngoại lệ mà bạn không thể bắt được bằng cách viết mã kiểu C, trước khi có thể vào free
bộ nhớ đó. Vì vậy, bất cứ khi nào bạn có một tập tin đó được xây dựng như C ++ với một .cpp
phần mở rộng hoặc bất cứ điều gì và nó làm tất cả các loại của những thứ như malloc
, memcpy
, memset
,qsort
, v.v., sau đó, nó sẽ yêu cầu các vấn đề tiếp theo nếu không phải là chi tiết triển khai của một lớp chỉ hoạt động với các kiểu nguyên thủy, tại thời điểm đó nó vẫn cần xử lý ngoại lệ để an toàn ngoại lệ. Nếu bạn đang viết mã C ++ bạn thay vì muốn thường dựa vào RAII và sử dụng những thứ như vector
, unique_ptr
, shared_ptr
, vv, và tránh tất cả các C-phong cách bình thường mã hóa khi có thể.
Lý do bạn có thể chơi với lưỡi dao cạo trong các loại dữ liệu C và tia X và chơi với bit và byte của chúng mà không dễ gây ra thiệt hại tài sản thế chấp trong một đội (mặc dù bạn vẫn có thể thực sự làm tổn thương chính mình) không phải vì C loại có thể làm, nhưng vì những gì họ sẽ không bao giờ có thể làm. Khoảnh khắc bạn mở rộng hệ thống loại C để bao gồm các tính năng C ++ như ctor, dtor và vtables, cùng với xử lý ngoại lệ, tất cả mã C thành ngữ sẽ được hiển thị xa hơn, nguy hiểm hơn nhiều so với hiện tại và bạn sẽ thấy một loại mới triết lý và tư duy phát triển sẽ khuyến khích một phong cách mã hóa hoàn toàn khác, như bạn thấy trong C ++, hiện đang xem xét ngay cả việc sử dụng một con trỏ thô sơ cho một lớp quản lý bộ nhớ trái ngược với tài nguyên tuân thủ RAII như unique_ptr
. Tư duy đó đã không phát triển từ một cảm giác an toàn tuyệt đối. Nó phát triển từ những gì C ++ cần đặc biệt an toàn trước các tính năng như xử lý ngoại lệ dựa trên những gì nó chỉ cho phép thông qua hệ thống loại của nó.
Ngoại lệ-An toàn
Một lần nữa, thời điểm bạn ở trong vùng đất C ++, mọi người sẽ mong đợi mã của bạn là ngoại lệ an toàn. Mọi người có thể duy trì mã của bạn trong tương lai, vì nó đã được viết và biên dịch bằng C ++ và chỉ cần sử dụng std::vector, dynamic_cast, unique_ptr, shared_ptr
, v.v. trong mã được gọi trực tiếp hoặc gián tiếp bởi mã của bạn, tin rằng mã đó là vô hại vì mã của bạn đã "được cho là" C ++ mã. Tại thời điểm đó, chúng ta phải đối mặt với cơ hội rằng mọi thứ sẽ ném, và sau đó khi bạn lấy mã C hoàn toàn tốt và đáng yêu như thế này:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future.
...
free(x);
return success;
}
return fail;
}
... Bây giờ nó đã bị hỏng. Nó cần phải được viết lại để được an toàn ngoại lệ:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
try
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future (maybe someone used
// std::vector inside of it).
}
catch (...)
{
free(x);
throw;
}
...
free(x);
return success;
}
return fail;
}
Tổng! Đó là lý do tại sao hầu hết các nhà phát triển C ++ sẽ yêu cầu điều này thay vào đó:
void some_func(int n, ...)
{
vector<int> x(n);
f(x); // some function which, now being a C++ function, may throw today
// or in the future.
}
Trên đây là mã an toàn ngoại lệ tuân thủ RAII thuộc loại mà các nhà phát triển C ++ thường chấp thuận vì hàm này sẽ không rò rỉ dòng mã nào kích hoạt lối thoát ngầm do kết quả của a throw
.
Chọn một ngôn ngữ
Bạn nên nắm lấy hệ thống và triết lý loại của C ++ với RAII, ngoại lệ, an toàn, mẫu, OOP, v.v. hoặc nắm lấy C, chủ yếu xoay quanh các bit và byte thô. Bạn không nên hình thành một cuộc hôn nhân không lành mạnh giữa hai ngôn ngữ này, và thay vào đó tách chúng thành các ngôn ngữ riêng biệt để được đối xử rất khác nhau thay vì làm mờ chúng lại với nhau.
Những ngôn ngữ muốn kết hôn với bạn. Bạn thường phải chọn một thay vì hẹn hò và lừa dối cả hai. Hoặc bạn có thể là một người đa thê như tôi và kết hôn với cả hai, nhưng bạn phải thay đổi hoàn toàn suy nghĩ của mình khi dành thời gian cho nhau và giữ họ cách xa nhau để họ không đánh nhau.
Kích thước nhị phân
Vì tò mò, tôi đã thử thực hiện danh sách miễn phí và điểm chuẩn của mình ngay bây giờ và chuyển nó sang C ++ vì tôi thực sự tò mò về điều này:
[...] Không biết nó trông như thế nào đối với C vì tôi chưa sử dụng trình biên dịch C.
... Và muốn biết liệu kích thước nhị phân có tăng lên hay không chỉ là xây dựng như C ++. Nó yêu cầu tôi phải rắc các diễn viên rõ ràng ở khắp nơi, nơi mà tôi thích viết những thứ cấp thấp như cấp phát và cấu trúc dữ liệu trong C tốt hơn) nhưng chỉ mất một phút.
Đây chỉ là so sánh bản dựng phát hành 64 bit MSVC cho một ứng dụng bảng điều khiển đơn giản và với mã không sử dụng bất kỳ tính năng C ++ nào, thậm chí không quá tải toán tử - chỉ là sự khác biệt giữa xây dựng nó với C và sử dụng, <cstdlib>
thay vì <stdlib.h>
và những thứ như vậy, nhưng tôi đã ngạc nhiên khi thấy nó không khác biệt gì so với kích thước nhị phân!
Nhị phân là 9,728
byte khi nó được xây dựng trong C và tương tự 9,278
byte khi được biên dịch dưới dạng mã C ++. Tôi thực sự không mong đợi điều đó. Tôi nghĩ những thứ như EH ít nhất sẽ thêm một chút ở đó (nghĩ rằng ít nhất nó sẽ giống như một trăm byte khác nhau), mặc dù có lẽ nó có thể hình dung rằng không cần thêm các hướng dẫn liên quan đến EH vì tôi chỉ cần sử dụng thư viện chuẩn C và không có gì ném. Tôi đã nghĩ một cái gì đósẽ thêm một chút vào kích thước nhị phân, như RTTI. Dù sao, thật tuyệt khi thấy điều đó. Tất nhiên tôi không nghĩ bạn nên khái quát từ kết quả này, nhưng ít nhất nó đã gây ấn tượng với tôi một chút. Nó cũng không ảnh hưởng đến điểm chuẩn, và một cách tự nhiên, vì tôi tưởng tượng kích thước nhị phân kết quả giống hệt nhau cũng có nghĩa là hướng dẫn máy giống hệt nhau.
Điều đó nói rằng, ai quan tâm đến kích thước nhị phân với các vấn đề an toàn và kỹ thuật được đề cập ở trên? Vì vậy, một lần nữa, chọn một ngôn ngữ và nắm lấy triết lý của nó thay vì cố gắng để khốn khổ nó; đó là những gì tôi khuyên bạn nên
//
ý kiến. Nếu nó hoạt động, tại sao không?