Video: How to learn to code (quickly and easily!) 2025
Stephen R. Davis
C ++ không phải là ngôn ngữ lập trình dễ dàng để làm chủ. Chỉ qua trải nghiệm, vô số những sự kết hợp của các biểu tượng bắt đầu có vẻ tự nhiên đối với bạn. Tuy nhiên, Cheat Sheet này cung cấp cho bạn một số mẹo vững chắc về việc nới lỏng việc chuyển đổi từ C ++ sang C ++ guru: Biết cách đọc các biểu thức phức tạp của C ++; học cách tránh các vấn đề về con trỏ; và tìm ra làm thế nào và khi nào để làm cho bản sao sâu.
Làm thế nào để đọc một Complex C + + Expression
C + + có đầy đủ các biểu tượng nhỏ, mỗi trong số đó thêm vào ý nghĩa của các biểu thức. Các quy tắc của ngữ pháp C + + rất linh hoạt mà các ký hiệu này có thể được kết hợp trong hầu hết các kết hợp phức tạp gần như impenetrably. Các biểu hiện bằng ngôn ngữ C đơn giản có thể trở nên mơ hồ đến nỗi đã từng là một cuộc thi hàng năm để ai có thể viết chương trình tối nghĩa và ai có thể hiểu nó.
Bạn không bao giờ nên cố gắng viết mã phức tạp nhưng đôi khi bạn sẽ chạy qua các biểu thức trong C ++ mà lúc đầu nhìn thoáng một chút. Chỉ cần sử dụng các bước sau để tìm ra chúng:
-
Bắt đầu từ ngoặc đơn được nhúng nhiều nhất.
Bắt đầu tìm kiếm dấu ngoặc ngoài nhất. Trong đó, tìm kiếm các dấu nháy được nhúng. Lặp lại quá trình cho đến khi bạn đã làm theo cách của bạn để cặp ngoặc sâu nhất. Bắt đầu đánh giá cụm từ này bằng cách sử dụng các quy tắc sau. Một khi bạn hiểu được biểu hiện đó, pop trở lại mức độ tiếp theo và lặp lại quá trình.
-
Trong cặp ngoặc đơn, đánh giá mỗi thao tác theo thứ tự ưu tiên.
Thứ tự các toán tử được đánh giá được xác định bởi sự ưu tiên của người vận hành được thể hiện trong bảng. Indirection đến trước khi phép nhân mà đi kèm trước khi bổ sung như vậy sau đây cho biết thêm 1 cộng với 2 lần giá trị chỉ vào bởi * ptr.
int i = 1 + 2 * * ptr;
Nhu cầu | Nhà điều hành | Ý nghĩa |
---|---|---|
1 | () (unary) | Gọi chức năng |
2 | * và -> (9)> + Deeference một con trỏ | 2 |
- (unary) | Trả về âm của đối số | 3 |
++ (unary) | Tăng | 3 > |
(nhị phân) | Phòng | 4 |
% (nhị phân) | Modulo | |
5 | + (nhị phân) | Bổ sung |
5 | - (nhị phân) | Trừ |
6 | & & (nhị phân) | Logical AND |
6 | ! ! | Logic OR |
7 | =, * =,% =, + =, - = (đặc biệt) | Các loại bài tập |
Đánh giá các hoạt động có cùng độ ưu tiên từ trái sang phải (trừ bài tập, mà đi theo cách khác). | Hầu hết các toán tử cùng độ ưu tiên đánh giá từ trái sang phải. Như vậy sau đây thêm 1 đến 2 và thêm kết quả vào 3: | int i = 1 + 2 + 3; |
Trình tự đánh giá của một số toán tử không quan trọng. Ví dụ: bổ sung hoạt động giống nhau từ trái sang phải như nó làm từ phải sang trái. Thứ tự đánh giá làm cho rất nhiều sự khác biệt cho một số hoạt động như chia. Sau đây chia 8 cho 4 và phân chia kết quả bằng 2: | int i = 8/4/2; | Ngoại lệ chính đối với quy tắc này là chuyển nhượng, được đánh giá từ phải sang trái: |
-
a = b = c;
Điều này gán c cho b và kết quả cho a.
Đánh giá các phản ứng phụ theo thứ tự không cụ thể.
Xem biểu thức sau:
int i = f () + g () * h ();
Nhân có độ ưu tiên cao hơn, vì vậy bạn có thể giả sử rằng các hàm g () và h () được gọi trước f (), tuy nhiên, đây không phải là trường hợp. Cuộc gọi hàm có mức ưu tiên cao nhất, vì vậy cả ba chức năng này được gọi trước khi phép nhân hoặc bổ sung được thực hiện. (Các kết quả thu được từ g () và h () được nhân lên và sau đó thêm vào kết quả trả về từ f ().
Thời gian duy nhất mà thứ tự các chức năng được gọi là tạo ra sự khác biệt là khi hàm có các phản ứng phụ chẳng hạn như mở một tập tin hoặc thay đổi giá trị của một biến toàn cầu. Bạn nên chắc chắn không viết các chương trình của bạn để chúng phụ thuộc vào các loại tác dụng phụ.
Thực hiện bất kỳ chuyển đổi kiểu nào chỉ khi cần thiết.
-
Bạn không nên thực hiện chuyển đổi nhiều loại hơn là hoàn toàn cần thiết. Ví dụ, biểu thức sau đây có ít nhất ba và có thể là bốn loại chuyển đổi:
float f = 'a' + 1;
Các char 'a' phải được thăng để một int để thực hiện bổ sung. Int này sau đó được chuyển thành một đôi và sau đó xuống chuyển đổi sang một phao chính xác duy nhất. Hãy nhớ rằng tất cả các số học được thực hiện hoặc là trong int hoặc double. Nói chung, bạn nên tránh thực hiện phép tính số đối với các loại ký tự và tránh sự trôi nổi chính xác duy nhất.
5 cách để tránh các vấn đề về con trỏ Trong C ++
Trong C ++, một con trỏ
-
là một biến có chứa địa chỉ của một đối tượng trong bộ nhớ trong của máy tính. Sử dụng các bước sau để tránh vấn đề với con trỏ trong C ++:
Khởi tạo con trỏ khi khai báo.
Không bao giờ để các biến con trỏ không chủ động - mọi thứ sẽ không quá tệ nếu các con trỏ uninitialized luôn chứa các giá trị ngẫu nhiên - phần lớn các giá trị ngẫu nhiên là các giá trị con trỏ bất hợp pháp và sẽ làm cho chương trình sụp đổ ngay khi chúng được sử dụng. Vấn đề là các biến uninitialized có xu hướng lấy giá trị của các biến con trỏ khác, trước đây được sử dụng. Những vấn đề này rất khó để gỡ lỗi.
Nếu bạn không biết cái gì khác để khởi tạo một con trỏ đến, khởi tạo nó để nullptr. nullptr được đảm bảo là một địa chỉ bất hợp pháp.
Không có điểm đầu ra sau khi bạn sử dụng chúng.
Tương tự như vậy, luôn luôn không biến con trỏ khi con trỏ không còn hợp lệ bằng cách gán giá trị nullptr. Điều này đặc biệt xảy ra khi bạn trả lại một khối bộ nhớ cho heap sử dụng xóa; luôn luôn không con trỏ sau khi trở lại bộ nhớ heap. Phân bổ bộ nhớ từ heap và trả lại nó cho heap cùng một mức "level" để tránh bị rò rỉ bộ nhớ. Luôn luôn cố gắng trả lại khối bộ nhớ cho heap ở mức độ trừu tượng như bạn đã phân bổ. Điều này thường có nghĩa là cố gắng để xóa bộ nhớ ở cùng cấp độ của các cuộc gọi chức năng.
-
Catch một ngoại lệ để xóa bộ nhớ khi cần thiết.
Đừng quên rằng ngoại lệ có thể xảy ra bất cứ lúc nào. Nếu bạn có ý định bắt ngoại lệ và tiếp tục hoạt động (trái với việc để chương trình sụp đổ), hãy chắc chắn rằng bạn nắm bắt ngoại lệ và trả lại bất kỳ khối bộ nhớ nào cho đống trước khi con trỏ trỏ đến chúng vượt quá phạm vi và bộ nhớ là mất đi.
Đảm bảo rằng các loại khớp chính xác.
-
Luôn đảm bảo rằng các loại con trỏ phù hợp với loại yêu cầu. Không được làm lại con trỏ mà không có một số lý do cụ thể. Xem xét những điều sau:
void fn (int * p); void myFunc () {char c = 'a'; char * pC = & c; fn ((int *) pC);}
-
Chức năng trên biên dịch mà không có khiếu nại vì con trỏ ký tự pC đã được tái tạo lại thành một int * để khớp với khai báo của fn (int *); tuy nhiên, chương trình này chắc chắn sẽ không hoạt động. Hàm fn () mong đợi một con trỏ đến một số nguyên 32-bit và không phải là một số rinky-dink 8 bit char. Những loại vấn đề này rất khó phân loại.
Làm thế nào và khi nào để làm bản sao sâu trong C ++
-
Các lớp phân bổ tài nguyên trong constructor của chúng nên thường bao gồm một nhà xây dựng bản sao để tạo ra các bản sao của các tài nguyên này. Phân bổ một khối bộ nhớ mới và sao chép nội dung của bản gốc vào khối mới này được gọi là tạo bản sao
-
(trái với bản sao nông cạn mặc định). Sử dụng các bước sau để xác định cách thức và thời gian để sao chép sâu trong C ++:
Luôn tạo một bản sao sâu nếu nhà xây dựng phân bổ tài nguyên.
Theo mặc định, C ++ làm cho bản sao các vật thể "nông cạn" được gọi là các thành viên khi chuyển chúng sang các hàm hoặc như là kết quả của một bài tập. Bạn phải thay thế các toán tử sao chép nông cạn mặc định với bản sao chép sâu của chúng cho bất kỳ lớp phân bổ tài nguyên nào trong nhà xây dựng. Tài nguyên phổ biến nhất được phân bổ là heap memory được trả về bởi toán tử mới.
Luôn luôn bao gồm một destructor cho một lớp phân bổ tài nguyên.
Nếu bạn tạo một constructor phân bổ các tài nguyên, bạn phải tạo một destructor để phục hồi chúng. Không có ngoại lệ. Luôn luôn khai báo destructor ảo. Một lỗi phổ biến cho người mới bắt đầu là quên tuyên bố destructor của bạn ảo. Chương trình sẽ chạy tốt cho đến khi một số lập trình viên không ngờ đến cùng và kế thừa từ lớp học của bạn. Chương trình vẫn xuất hiện để làm việc, nhưng vì destructor trong lớp cơ sở có thể không được gọi đúng cách, bộ nhớ rò rỉ từ chương trình của bạn như một cái sàng cho đến khi nó cuối cùng bị treo. Vấn đề này rất khó tìm.
-
Luôn luôn bao gồm một hàm tạo bản sao cho một lớp phân bổ tài nguyên.
Nhà xây dựng bản sao tạo ra một bản sao thích hợp của đối tượng hiện tại bằng cách cấp phát bộ nhớ ra khỏi đống và sao chép nội dung của đối tượng nguồn.
-
Luôn ghi đè toán tử phân bổ cho một lớp phân bổ tài nguyên.
Các nhà lập trình nên không khuyến khích các toán tử trọng, nhưng toán tử gán là một ngoại lệ. Bạn nên ghi đè toán tử gán cho bất kỳ lớp nào phân bổ tài nguyên trong hàm tạo.
-
Toán tử gán phải làm ba việc:
Đảm bảo rằng đối tượng tay trái và tay phải không phải là cùng một đối tượng. Nói cách khác, hãy chắc chắn rằng lập trình viên ứng dụng không viết gì đó như (a = a). Nếu có, không làm gì cả.
-
Gọi mã giống như destructor trên đối tượng tay trái để trả lại tài nguyên của nó.
Gọi cùng một mã như một constructor sao chép để tạo một bản sao sâu của đối tượng tay phải vào đối tượng tay trái.
-
Nếu bạn không thể làm điều đó, sau đó xóa các nhà xây dựng bản sao và các nhà điều hành phân công để chương trình không thể làm cho bản sao của đối tượng của bạn.
Nếu bạn thậm chí không thể làm điều đó bởi vì trình biên dịch của bạn không hỗ trợ tính năng constructor của C ++ 2011 delete constructor, tạo ra một constructor và constructor để tạo ra sản phẩm rỗng và tuyên bố chúng được bảo vệ để giữ các lớp khác sử dụng chúng.