96 KiB
C++ Key Topics Summary for Final Review (C++ 知识点期末复习总结
Lecture 1: Introduction to C++ (第1讲:C++简介)
Key Concepts 主要知识点:
- Machine Language(机器语言) – The most basic programming language, consisting of binary code (0和1) directly executed by hardware. Machine code is machine-dependent(与硬件相关); a program written for one type of CPU won’t run on another.
- Assembly Language(汇编语言) – Uses short English-like mnemonics(助记符) to represent machine instructions. Requires an assembler(汇编器) to translate into machine code. Assembly is easier to read than binary but still specific to a CPU architecture.
- High-Level Language(高级语言) – A programming language with English-like syntax and math-like notation. Examples: C++, Java. High-level code is portable and must be converted to machine code by a compiler(编译器) or run by an interpreter(解释器). C++ is compiled into machine code (可编译成机器码执行), whereas Java is compiled to bytecode and run on the Java Virtual Machine (运行于JVM).
- Phases of C++ Program Compilation(C++程序的编译阶段) – C++ code undergoes several steps: preprocessing(预处理), compiling(编译), linking(连接), and loading(载入). The preprocessor(预处理程序) handles directives (like
#include) before compilation. The compiler(编译器) translates source code to object code (machine instructions). The linker(连接器) combines your object code with libraries to produce an executable. The loader(加载程序) then loads the executable into memory for execution. - Object-Oriented Programming (OOP, 面向对象编程) – C++ supports OOP, which involves organizing software as collections of classes(类) and objects(对象). It emphasizes concepts like encapsulation(封装), inheritance(继承), and polymorphism(多态). (Java is also OOP; C++ and Java share OOP concepts, but C++ allows lower-level memory control.)
After-class Exercises 课后练习:
- 填空题: 本章讨论的三种语言类型是 **、**** 和 ______。【提示】机器语言(machine language)、汇编语言(assembly language)和高级语言(high-level language)。
- 填空题: 在C++系统中,______ 程序在编译器开始翻译之前执行。【提示】预处理程序(preprocessor)。
- 填空题: ______ 程序将编译器的输出与各种库函数结合起来生成可执行程序。【提示】连接器(linker)。
- 填空题: ______ 程序将可执行程序从磁盘传送到内存中准备运行。【提示】加载程序(loader)。
Answers 答案: 1) 机器语言, 汇编语言, 高级语言; 2) 预处理程序; 3) 连接程序; 4) 加载程序。每个阶段各司其职,共同完成从源代码到可执行代码的转换。
Lecture 2: Basic Syntax and Printing (第2讲:C++基本语法与打印)
Key Concepts 主要知识点:
- Comments(注释) – Non-executing text used to document code.
//begins a single-line comment(单行注释), and/* ... */encloses a multi-line comment(多行注释). Comments improve readability and are ignored by the compiler (编译时会被忽略). (Java uses the same comment styles.) - Preprocessor Directive(预处理指令) – Lines beginning with
#(like#include <iostream>) are processed by the preprocessor before compilation. For example,#include <iostream>inserts the declarations for input/output streams (输入输出流库) so we can usestd::coutandstd::cin. In C++,<iostream>is analogous to Java’sjava.iopackage, but C++ uses include files instead of import statements. mainFunction(主函数) – The entry point of a C++ program. Defined asint main()typically. The keywordintindicates main returns an integer status code to the system. In C++,maincan be outside any class (不属于任何类), whereas in Java the entry point is apublic static void mainmethod within a class.- Statements and Semicolon(语句和分号) – A statement(语句) is an instruction terminated by a semicolon
;. C++ is a free-form language: you can use whitespace(空白字符) (spaces, tabs, newlines) to format code for readability; extra whitespace is generally ignored by the compiler. Each statement must end with; - Output with
std::cout(使用cout输出) – Usestd::cout << "text";to print to the console. The<<is the stream insertion operator(流插入运算符), sending data to the output stream. - Input with
std::cin(使用cin输入) –std::cinis the standard input stream (对应键盘输入). Using the extraction operator>>, as instd::cin >> x;, reads input into variablex. (Similar to Java’sScanner, but C++ I/O is type-safe and reads whitespace-delimited tokens by default.) - Variables and Memory(变量与内存) – A variable in C++ has a name (identifier, 标识符), a type(类型), and stores a value in memory. All variables must be declared before use(先声明后使用). Common primitive types:
int整型,double双精度浮点,char字符型,bool布尔型 (布尔值用true/false). C++ types correspond to Java’s primitive types, but note that boolean values in C++ can be implicitly converted to integers (true为1, false为0), whereas Java strictly separatesbooleanfrom numeric types. - Operators and Expressions(运算符与表达式) – C++ supports arithmetic operators (
+ - * / %), assignment operators (=and compound assignments like+=), and relational operators(关系运算符) (== != < > <= >=) for comparisons. Also logical operators (&& || !) for boolean logic. These work similarly to Java. One difference: In C++ you can use anintor other numeric in a condition (非零即真, 0为假), but in Java the condition of anifor loop must strictly be a boolean expression. ifStatement(条件语句) – Allows decision-making.ifexecutes a block if a condition is true.if...elsechooses one block if condition true, otherwise the else-block (如果条件为假则执行else部分). In C++, as in Java, conditions use==for equality (注意避免将=误用在条件判断中). C++99 introduced a bool type, so modern C++ usestrue/falsesimilarly to Java, but older C style (如使用0和1表示真假) is still valid.returnStatement(返回语句) – Ends a function and returns a value. Inmain,return 0;typically indicates successful execution. If omitted in C++main, it defaults to 0. (Java’smainisvoid, so it doesn’t return a value to the system.)
After-class Exercises 课后练习:
- 填空题: 每个C++程序的执行从函数 ______ 开始。【提示】C++程序从
main函数开始执行。 - 填空题: 大多数C++语句以 ______ 结束。【提示】C++语句以分号(
;)结束。 - 判断题: 转义序列
\n通过cout输出时会将光标移动到下一行开头。(对/错)【提示】\n表示换行,输出它会换行,因此表述正确。 - 判断题: 所有变量在使用之前都必须先声明。(对/错)【提示】C++要求变量先声明再使用,该说法正确。
Suggested Answers 答案: 1) main; 2) 分号 (即每条语句末尾加;); 3) 对。\n是换行符,会使输出换到新行; 4) 对。在C++中必须先定义变量类型和名称,然后才能赋值或使用它。请注意区分赋值运算符=和相等比较运算符==,避免在条件判断中误用。
Lecture 3: Classes, Objects and Strings (第3讲:类、对象与字符串)
Key Concepts 主要知识点:
- Class(类) – A blueprint for creating objects (对象模版). A class defines a new data type(数据类型) that groups data and functions. For example,
class GradeBook { ... };defines aGradeBookclass. A class contains data members(数据成员) (also called attributes 属性 or fields 字段) and member functions(成员函数) (also called methods 方法). By default, class members are private(私有) in C++ (only accessible by the class’s own functions). Public interface is declared after thepublic:label. (Java类默认成员可见性不同,Java中成员缺省是包可见,而C++类缺省是private。) - Object(对象) – An instance of a class. When you create an object, e.g.,
GradeBook myGradeBook;, it allocates memory for the data members and allows you to call member functions on it. Each object has its own copies of the class’s non-static data members (每个对象都有自己的一份数据成员). In OOP, an object’s data should usually be kept private, accessed only via the class’s public member functions (encapsulation 封装原则). - Member Function(成员函数) – A function defined inside a class, describing behaviors or operations on class data. For example,
displayMessage()might print a welcome message stored in aGradeBookobject. Member functions can be defined inside the class definition or separately (using the scope resolution operatorClassName::functionName). By default, member functions are not virtual(虚拟) unless specified (see Lecture 11 for polymorphism details). (Java的方法默认是虚拟(可覆盖)的,但C++需要明确使用virtual才能实现运行时多态。) - Access Specifiers(访问说明符) – Keywords like
public,private, andprotectedthat control access to class members.public公有 members are accessible from outside the class (接口API), whileprivate私有 members are hidden from outside (只能被该类的成员函数访问). C++ class definitions often list public functions first (interface) and keep data members private. (Java有类似的public/private修饰符,还有protected和默认访问级别; C++的protected在继承时用,后续讲解。) - CamelCase Naming(驼峰命名) – By convention, C++ class names start with a capital letter and use capital letters to distinguish words (e.g.,
GradeBook), whereas function names typically start with lowercase (e.g.,displayMessage). This is a stylistic convention to improve code readability (在代码中区分类名和函数名). Java uses a similar convention (类名首字母大写,方法名首字母小写). - Constructors(构造函数) – A special class member function that has the same name as the class and is used to initialize new objects (初始化对象). A default constructor(默认构造函数) is one that can be called with no arguments (either because it has no parameters or all parameters have default values). Constructors have no return type (not even void). In C++, if you do not define a constructor, the compiler provides a default constructor. Constructors are often used to set initial values for data members when an object is created.
stringClass(字符串类) – C++ providesstd::string(in<string>header) to represent text strings. You can create strings likestring s = "hello";and uses.length()ors.size()for length,s[0]for character access, etc. This is similar to Java’sStringclass, but C++std::stringis mutable (可变的) and uses value semantics (copy on assignment) unless otherwise specified. Older C-style strings use char arrays terminated by'\0', butstd::stringis safer and easier. (Java的String是不可变对象; C++的std::string可以修改内容,例如s.append("!");添加字符。)- Getters and Setters(访问器和修改器) – It’s common to provide public member functions to get and set private data members. For example, a
GradeBookclass might havegetCourseName()andsetCourseName()to access a privatecourseNameattribute. This preserves encapsulation by controlling how outside code interacts with class data. (Java亦如此,通常用getX/setX方法访问私有字段。) - Separating Interface and Implementation(头文件与实现) – C++ practice is to put class definitions in a header file (e.g.,
GradeBook.h) and function implementations in a.cppfile, then include the header where needed. This separation allows multiple source files to include the class definition without duplication. Java does not separate interface and implementation in the same way; Java class definitions (fields and method signatures and bodies) are all in one.javafile, but C++ encourages dividing the declaration (.h) and implementation (.cpp).
After-class Exercises 课后练习:
- 填空题: 每个类定义都以关键字 ______ 开头,后面紧跟类名。【提示】定义类以
class开头,例如class ClassName。 - 填空题: 类定义通常存储在文件以 ______ 扩展名结尾的文件中,以便被多处引用。【提示】头文件通常以
.h或.hpp结尾。 - 填空题: 当每个对象都有自己的某个属性副本时,这种属性变量也称为 ______ 。【提示】实例成员变量,即数据成员。
- 填空题: 关键字
public是一种 ______ 。【提示】public是访问说明符(access specifier)。 - 判断题: 按照惯例,函数名应以大写字母开头,每个单词首字母大写。(对/错)【提示】函数名一般不以大写开头,而是首字母小写,所以该说法是错的。
- 判断题: 函数定义中紧随函数名的空括号表示该函数不需要任何参数即可执行。(对/错)【提示】空参数列表表示无参数,该说法正确。
- 判断题: 用
private声明的数据成员或成员函数对该类的其他成员函数也是不可见的。(对/错)【提示】私有成员对本类的函数是可访问的,对类外才不可见,所以表述错误。实际上,private成员可以被本类的成员函数访问。 - 判断题: 在某成员函数内部定义的变量称为数据成员,可以被该类的所有成员函数使用。(对/错)【提示】在函数内部定义的变量是局部变量,只能在该函数内部使用,不是整个类的成员,所以错误。
- 判断题: 每个函数体都由左花括号
{开始、右花括号}结束。(对/错)【提示】函数体需用{}包围,正确。 - 编程题: Invoice(发票)类设计:创建一个名为Invoice的类以表示商店售出的某项商品的发票。这个类应包含以下私有数据成员: 商品编号(字符串), 商品描述(字符串), 购买数量(int), 单价(int)。为每个数据成员提供成对的
set和get函数。此外,提供一个构造函数用于初始化所有成员,并提供一个名为getInvoiceAmount的成员函数来计算发票总额(即数量乘以单价)并返回该值(int)。如果购买数量或单价为负,则将其设为0再计算总额。要求: 翻译以上需求,并给出实现提示。
Lecture 4: Control Statements (第4讲:控制语句)
Key Concepts 主要知识点:
-
Algorithm(算法) – A procedure or formula for solving a problem, consisting of a sequence of actions in a specific order (一系列有序执行的操作). Writing an algorithm in simple steps before coding helps in program design.
-
Pseudocode(伪代码) – An informal, English-like way to describe an algorithm’s steps. Pseudocode is not actual code; it omits syntactic details and focuses on logic. It helps to plan programs without worrying about language syntax. (书写伪代码有助于先理清思路,再将其转换为真正的C++代码。)
-
Control Structures(控制结构) – Basic constructs that dictate the flow of program execution. All programs can be built using three fundamental structures: sequence(顺序执行), selection(选择执行), and iteration(循环执行). C++ (like Java) has corresponding control statements(控制语句) for each:
- Sequence 顺序结构: The default execution order where statements run one after another in the order written (顺序执行每条语句).
- Selection 选择结构: Making decisions. C++ provides
if单分支 andif...else双分支 statements, and theswitch多分支 statement for multi-way branching. Theifstatement executes a block if a condition is true;if...elsechooses between two blocks. Theswitchstatement tests an integer (or enum/char) expression and executes the case matching that value (eachcase标签后跟要执行的语句块). Java’sswitchis similar but can also use strings; C++17开始也允许switch字符型或枚举型,传统C++中switch不支持字符串。 - Iteration 循环结构: Repeating actions. C++ has
whileandforloops (可能执行零次或多次) and ado...whileloop (至少执行一次). Awhileloop(当型循环) repeats as long as a condition remains true (先检查条件,再执行循环体). Aforloop(计数循环) is typically used for a known number of iterations, with initialization, condition, and increment in one line. Ado...whileloop(后测试循环) executes the body first, then checks the condition to decide if it should repeat. In all these, a loop condition works like anifcondition: in C++ nonzero means true, zero means false. (Java的循环与C++基本一致,也有while,for,do...while,但注意Java中必须使用布尔表达式作为循环条件,C++中整数也可作为条件使用。)
-
ifandif...else(条件语句) – Theifstructure allows conditional execution. Syntax:if (condition) { // true block } else { // false block }If the condition(条件) is true (非0或
true), the first block executes; otherwise theelseblock executes (if present). Use braces{}to group multiple statements in a block. 良好习惯:即使只有一条语句,也使用花括号以避免歧义(Good practice to always use braces for clarity). You can nest ifs for multiple levels of decision (嵌套的if结构处理更复杂的条件). -
switchStatement(多路分支) – Used for multi-way branching based on an integer or enum expression. Syntax:switch (expression) { case constant1: // statements break; case constant2: // statements break; ... default: // statements }The
caselabels(分支标签) must be constant expressions. Theswitchtransfers control to the matching case. Without an explicitbreak, execution will “fall through” to the next case (贯穿后续case). Typically, each case ends withbreakto prevent fall-through (除非有意连续执行). Thedefaultlabel handles any value not matched by cases. (Java的switch相似,但在Java中使用字符串作为case是允许的,而传统C++不直接支持字符串。无论C++还是Java,都需要break避免贯穿。) -
Loops(循环) – Three types in C++:
-
whileloop(当型循环): Checks condition first, then executes loop body if condition is true. Continues checking and executing until condition becomes false. Example:while (x > 0) { x--; }. If condition is initially false, the loop body may not execute at all. -
do...whileloop(至少执行一次的循环): Executes the loop body at least once and then checks the condition at the end. Syntax:do { // body } while(condition);Useful when the loop body should run at least one time (例如菜单处理,需要先显示菜单再判断是否重复).
-
forloop(计数循环): Typically used when the number of iterations is known or to iterate over a range. Syntax:for ( initialization; condition; increment ) { // body }Example:
for(int i=0; i<5; ++i) { ... }will loop 5 times (i从0到4). Theforloop is essentially a compact form of a while: initialization executes once at loop start, then each iteration checks condition and executes body, then performs increment step. C++11 adds a range-based for loop(基于范围的for循环), e.g.,for(int val : array) { ... }, to iterate over containers/arrays easily (类似Java增强for循环).
-
-
breakandcontinue– Within loops (以及switch中),break(跳出) immediately exits the loop (终止当前循环), andcontinue(继续下轮) skips the remaining statements in the loop body and proceeds with the next iteration (提前进入下一次循环判断). Use these carefully to avoid confusing flow. (Java中break/continue语义相同。) -
Logical Operators(逻辑运算符) –
&&(AND),||(OR), and!(NOT) are used to form complex conditions (组合多个条件). They work similarly to Java, including short-circuit evaluation (短路求值): inexpr1 && expr2, ifexpr1is false,expr2is not evaluated at all. Likewise for||: if the first operand is true, the second is skipped. Use parentheses to clarify logic when needed. -
Compound Assignment(复合赋值) – Operators like
+=,-=,*=,/=,%=for combining arithmetic with assignment. E.g.,x += 5;adds 5 to x (相当于x = x + 5;). These make code concise. C++ also has increment/decrement operators(自增/自减):++xorx++to add 1,--xorx--to subtract 1. Prefix (++x) vs postfix (x++) matters in expressions (前置返回新值, 后置返回旧值), but as standalone statements both increment x. (Java also has these operators with the same behavior.) -
Data Type Portability(数据类型可移植性) – An awareness point: Fundamental types like
intmay have different sizes on different platforms (e.g., 32-bit vs 64-bit systems). C++ defines minimum ranges (e.g.,intat least ±32,767) but not exact sizes in the language standard. Java’s primitive types have fixed sizes (e.g., int is always 32-bit) making them more portable across platforms. C++11 introduced fixed-width types (如int32_t) in<cstdint>for precise control when needed. -
Boolean context(布尔上下文): In C++, an
intor pointer can be used in a boolean context (非零即真), whereas Java requires a true boolean value for conditions (在Java中使用非boolean作为条件会编译错误). For example,while(n)is valid in C++ (loops while n≠0), but in Java you must writewhile(n != 0). -
Switch limitations(switch的限制): C++
switchcan use integral types (int/char/enum) but not strings or objects. Java’sswitch(since Java 7) allowsStringand wrapper types. Also, C++ does not automatically break at the end of a case (必须手工break防止贯穿), same as Java. Both languages support adefaultcase. -
Loop features: Both anguages support
breakandcontinueinside loops. Java adds labeled breaks/continues; C++ doesn’t have labeled loop control (no equivalent to Java’slabel:with break label). -
for-each loop: Modern C++ (
range-based for) is akin to Java’s enhanced for loop for arrays/collections. Syntax differs slightly but concept is the same (iterate through elements directly). -
do...while: Present in both languages with identical behavior (loop executes at least once).
-
No implicit type conversion to boolean in Java: Java不会将数字自动转为布尔,这在C++中需要注意,如果将C++代码移植到Java,要显式比较==0或!=0。
-
Precision of types: As mentioned, Java’s types have fixed size (e.g.,
int32-bit), whereas C++’s may vary; this can affect loop ranges or portability if you assume a certain size in C++.
After-class Exercises 课后练习:
-
判断题: 在
if语句中,若条件不加括号也能正常编译运行。(对/错)【提示】条件必须放在圆括号内,漏写括号会导致语法错误,错误。正确格式如:if (x > 0) {...}。 -
判断题: 在
switch的每个case分支后都应使用break来防止执行落入下一个分支。(对/错)【提示】一般应加break避免“贯穿”行为,正确。如需特意贯穿,可以省略,但必须十分小心。 -
判断题: 在
for循环中使用continue语句会提前跳出整个循环。(对/错)【提示】continue只是跳过本次余下循环并进入下一次迭代,不会终止循环,错误。结束整个循环应使用break。 -
编程题: 判断回文数(Palindrome) – 定义一个函数
bool isPalindrome(string str),如果字符串正着读和反着读都相同则返回true,否则返回false。例如:"madamimadam"是回文。请将此函数补充完整。提示: 所谓“回文”指序列从前往后和从后往前相同。解决思路:可以使用两个索引分别从字符串头部和尾部向中间移动,逐字符比较是否相等。一旦发现对应字符不相等,就可以断定不是回文。若所有字符成对匹配,则是回文。如:
bool isPalindrome(string str) { int i = 0, j = str.length() - 1; while(i < j) { if(str[i] != str[j]) return false; i++; j--; } return true; }此函数遍历字符串的一半长度即可完成判断,时间复杂度为O(n)。在调用时可传入数字的字符串形式进行判断(也可以先将int转换为string再调用)。(In Java, one could similarly check by comparing
str.charAt(i)andstr.charAt(j)in a loop.)
Lecture 5: Functions, Recursion and Overloading (第5讲:函数、递归与重载)
Key Concepts 主要知识点:
- Functions(函数) – Named blocks of code that perform a specific task, improving modularity (模块化) and reuse. Functions in C++ can be standalone (free) functions(自由函数) or member functions of classes. A function definition(定义) consists of a return type, name, parameter list, and body. A function call(调用) executes the function’s body. For example,
int add(int a, int b) { return a+b; }defines a functionaddthat returns an int sum, and you call it asint result = add(3,4);. Functions allow divide-and-conquer programming by breaking a program into smaller pieces. (Java的方法类似,但Java没有独立的全局函数,所有方法必须属于类;C++既有类成员函数也允许全局函数定义。) - Function Prototype(函数原型) – A function declaration that specifies the function’s interface (name, return type, parameters) without the body. For example,
int add(int, int);is a prototype. Prototypes (often placed in headers) allow the compiler to know about a function before its definition appears, enabling calls from code that comes lexically before the function’s definition. The compiler uses the prototype to verify calls (参数类型和数量是否匹配) and perform type coercion(类型转换) for arguments if needed. In C++, you must either declare or define a function before calling it (Java doesn’t require forward declarations because the one-pass compiler or JIT knows all class methods by design). - Parameters and Arguments(形参与实参) – Parameters(形式参数) are variables declared in the function definition/prototype to receive data, and arguments(实参) are the actual values passed to the function when called. The number, order, and types of arguments must match the function’s parameters (except where default parameters are used). C++ evaluates arguments left-to-right (evaluation order unspecified in older C++ standards, but guaranteed in C++17). By default, C+。+ uses pass-by-value(值传递), meaning the function gets a copy of the arguments (函数操作形参不会影响调用者提供的实参变量). You can also use pass-by-reference(引用传递) or pointers to allow the function to modify caller variables (e.g.,
void foo(int &x) { x = 5; }). (Java方法参数传递都是值传递,但对象引用作为值传递可以间接修改对象的内容。Java没有C++引用这种语法,但对象参数可改变其内部状态。) - Return Value(返回值) – The value a function gives back after completion, specified by the function’s return type. Use the
returnstatement to return a value and exit the function. If a function is declared with a non-void type, it should return a value of that type. If the function isvoid, it does not return a value (though it can usereturn;to exit early). In C++, failing to return a value from a non-void function results in undefined behavior (Java would not compile such a method). - Void Functions(void函数) – Declared with
voidreturn type, they do not return any value. They typically perform actions like printing or modifying global state. For example,void printMessage() { cout<<"Hi"; }. You call it asprintMessage();and there’s no result to use. (Java’svoidmethods are analogous.) - Local and Global Variables(局部和全局变量) – Local variables(局部变量) are those defined inside a function (including parameters), which exist only during function execution and are not known outside. Each call creates new instances of local variables (stored on the stack 栈). Global variables(全局变量) are defined outside any function, accessible from any code after their declaration (unless restricted by
staticor namespaces). Global variables in C++ have program duration (allocated in static storage). It’s good practice to minimize globals for modularity. - Storage Class and Lifetime(存储类与生命周期) – C++ variables have storage durations: automatic(自动存储期) for locals (allocated on function entry, freed on exit), static(静态存储期) for globals and
staticlocal variables (allocated when program starts or when first encountered for local statics, freed at program end), and dynamic(动态存储期) for memory allocated vianew(lives untildelete). For example, astatic int count;inside a function retains its value between calls (第一次调用时初始化一次,以后调用保留之前的值). Java manages object lifetimes with garbage collection and has no direct equivalent to C++ manual memory allocation in terms of keywords (nonew/deletepairing visible to programmer; Java’snewautomatically managed by GC). - Math Library Functions(数学库函数) – C++
<cmath>header provides common functions likesqrt(),pow(),sin(), etc. These are global (not class members) and can be called by including<cmath>and writing e.g.double r = sqrt(4.0);. (Java has similar math functions injava.lang.Mathas static methods, e.g.,Math.sqrt(4.0)). - Function Overloading(函数重载) – C++ allows multiple functions with the same name but different parameter lists (不同的参数签名) in the same scope. The compiler distinguishes them by their signature(函数签名) – the combination of function name and parameter types (顺序及数量). For example, you can have
int max(int,int)anddouble max(double,double). When you callmax(3,5), the int version is invoked;max(3.5, 4.2)calls the double version. Overloading lets you use a common name for conceptually similar operations on different types. (Java also supports method overloading in classes. In both languages, return type alone is not enough to overload; 参数列表必须不同。) The compiler uses name mangling(名字改编) under the hood to encode parameter types in the symbol name, enabling type-safe linkage. - Default Arguments(默认参数) – C++ allows you to specify default values for parameters in function declarations. For instance,
void print(int count = 1)means ifprint()is called without an argument,countwill default to 1. Default arguments must be the rightmost parameters, and if you omit one argument in a call, you must omit all to its right. Java does not have default parameters; instead, you overload methods to achieve similar behavior. (注意:默认参数值在函数声明处给出,在调用时若不传该参数则使用默认值。) - Inline Functions(内联函数) – Declared with the keyword
inline(or defined directly in class definition for member functions), these suggest to the compiler to expand the function body at the call site to avoid function call overhead. Useful for very short functions. Modern compilers optimize short functions automatically, so explicitinlineis less critical. (Java inlining is handled by the JIT at runtime and not exposed in source.) - Recursion(递归) – A programming technique where a function calls itself (直接或间接调用自身). Recursive solutions have a base case(基例) that stops recursion and one or more recursive cases(递归情况) where the function calls itself with a smaller sub-problem. For example, the factorial function can be defined recursively:
factorial(n) = n * factorial(n-1)with base casefactorial(0)=1. Each recursive call has its own set of local variables (stack frame) on the function call stack(调用栈). If recursion doesn’t converge to a base case, it results in infinite recursion (类似无限循环) and eventually a stack overflow (栈内存溢出). Recursion can produce elegant solutions for problems like factorial, Fibonacci, tree traversal, etc., but iterative solutions (使用循环) may be more efficient in some cases. C++ and Java both support recursion similarly. - Recursive vs Iterative(递归与迭代对比) – Both recursion and looping achieve repetition. Iteration(迭代) explicitly uses loops, modifying loop variables until a condition fails. Recursion uses repeated function calls, letting the runtime handle looping via the call stack. Recursion can be more readable for divide-and-conquer algorithms (分治算法), but each function call has overhead. For example, calculating Fibonacci recursively leads to many duplicate subcalls, whereas an iterative approach or memoization is more efficient. It’s important to choose the right approach based on clarity and performance. (在Java中也是如此,递归容易栈溢出且有函数调用开销,但写法简洁; 有些算法(如遍历树结构)递归实现更直观。)
After-class Exercises 课后练习:
-
填空题: 只在定义所在函数内可见的变量称为 ______ 变量。【提示】这类变量是局部变量(local variable)。
-
填空题: 在被调用函数中使用 ______ 语句可以将表达式的值传回调用函数。【提示】使用
return语句返回值。 -
填空题: 函数头中使用关键字 ______ 表明该函数不返回任何值。【提示】
void关键字。 -
填空题: 可以定义多个同名但参数不同的函数供不同数据类型使用,这称为函数 ______ 。【提示】函数重载 (function overloading)。
-
填空题: 使用限定符 ______ 声明的变量为只读变量(不能被修改)。【提示】用
const限定符声明常量。 -
判断题: 一个类可以定义多个与类同名的函数来构造对象。(对/错)【提示】对。类可以有多个构造函数,参数不同即构成重载。
-
判断题: 如果函数参数需要多个值,那么函数调用时参数和形参在类型、顺序和数量上必须一一对应。(对/错)【提示】对。实参会按照位置依次匹配形参,类型必须兼容或可转换,否则编译错误。
-
判断题: 用关键字
inline声明的函数编译后一定会内联展开,不会发生函数调用开销。(对/错)【提示】错。inline只是建议,具体是否内联由编译器决定。另外,即使没声明inline,编译器有时也会优化内联。 -
判断题: 递归和迭代都会重复执行操作,但递归可能导致较高的函数调用开销和内存占用。(对/错)【提示】对。递归调用频繁压栈出栈,开销较大;迭代通常更节省资源。如果递归层次太深还可能导致栈溢出。
-
找错改错: 下列代码包含若干错误,请说明并更正。
void g() { cout << "Inside function g" << endl; void h() { cout << "Inside function h" << endl; } } float cube(float); // function prototype cube(float number) { return number * number * number; }提示: 存在两个主要错误:首先,C++中不允许在函数
g内部定义另一个独立的函数h(不能在函数内嵌套定义函数)。应将h的定义移到g外部。同理,每个函数的花括号必须匹配,g函数在定义h之前就应该闭合其作用域。其次,cube(float number)函数的定义缺少返回类型,应与前面的原型一致返回float。修改如下:void g() { cout << "Inside function g" << endl; } void h() { cout << "Inside function h" << endl; } float cube(float number) { return number * number * number; }以上更正包括:将
h定义为独立的void h()函数,并为cube函数添加返回类型float使其与声明匹配。此外,确保每个函数的定义都有匹配的花括号闭合。经过修改,代码可正常编译,函数调用关系也清晰明确。
Lecture 6: Arrays and Vectors (第6讲:数组与向量)
Key Concepts 主要知识点:
- Array(数组) – A collection of elements of the same type(相同类型) stored contiguously in memory. Think of an array as a series of numbered boxes, each holding a value of a particular type. You refer to an element by its index(索引/下标), an integer offset from the start of the array. In C++ (and many languages), array indexing starts at 0 (首元素索引为0). For example, if
int a[5];is an array of 5 ints, valid indices are 0 through 4. The number inside[]is the array’s length (元素数量).- Declaring Arrays(数组声明): Use syntax
Type arrayName[arraySize];. This allocates memory forarraySizeelements ofType. The size must be a constant expression (编译时常量) in C++ for static arrays. It’s recommended to use a constant or#definefor array sizes to avoid “magic numbers” (不直接使用字面值) in the code. - Accessing Elements(访问元素): Use the subscript operator
[], e.g.,a[2]refers to the third element of arraya. Indices must be integers (or integer expressions). Be careful: C++ does not perform bounds-checking(不自动检查下标越界) on array accesses. Accessing an out-of-range index causes undefined behavior (可能读取无效内存或导致程序崩溃). In contrast, Java throws an exception if you index out of bounds. Always ensure index0 <= index < arrayLength. - Initializing Arrays(数组初始化): You can initialize an array when you declare it. For example:
int b[3] = {1, 2, 3};orint c[5] = {0};(initializes all 5 elements to 0). If the initializer list has fewer elements than the array, remaining elements are initialized to 0 (implicitly). If the array size is omitted, the compiler deduces it from the initializer count. (Java数组初始化类似,但Java数组是对象,在运行时动态分配; C++静态数组在栈或静态存储上分配。) - Constant for Array Size(常量数组长度): It’s good practice to use a
constvariable for array size, e.g.,const int SIZE = 10; int arr[SIZE];. This makes the code more maintainable (eliminates “magic number”) and clearly communicates the array length. Note: In modern C++ you can also usestd::arrayorstd::vectorwhich manage size dynamically or at compile time.
- Declaring Arrays(数组声明): Use syntax
- Array Index (Subscript)(数组下标) – The index is the position of an element. Also called subscript(下标). The first element is index 0, last element index = size-1. Using an index outside this range leads to invalid memory access (C++不会自动抛异常,因此务必小心)。For example, if
int a[5];,a[5]is an invalid access (有效索引为0~4)。 - Memory Layout(内存布局) – Arrays are stored in contiguous memory. If
ais anint[5], and on a systemintis 4 bytes, thena[0]is at some addressp,a[1]is atp+4,a[2]atp+8, etc. Pointer arithmetic can be used with arrays (Lecture 7). C++ does not store the array length with the array (unlike Java which hasarray.lengthproperty). You must track the size separately. - Constant vs Variable Length Arrays: Traditional C++ (pre-C++11) required array size to be a compile-time constant for stack allocation. Some compilers allow variable length arrays (VLAs) as an extension, but the portable way for dynamic size is to allocate on heap or use
std::vector. (Java数组长度是在运行时确定的,通过new分配,C++也可以用new动态分配数组,或使用标准容器。) - Arrays Initialization Pitfalls(初始化注意): If you partially initialize (e.g.,
int d[5] = {1,2};), uninitialized elements are set to 0 in C++ for static storage or if initializer list is provided. However, if you omit an initializer entirely for a local array, the array contains garbage values(未定义垃圾值). Always initialize or set values before use. - const Arrays(常量数组): Declaring an array as
const(e.g.,const int arr[3] = {…};) makes its elements read-only. Alternatively, usingconstexprfor compile-time constant arrays. (Java has final arrays references, but array elements can still change unless individually final; C++ const truly makes each element constant.) - Multidimensional Arrays(多维数组) – C++ allows arrays of arrays, e.g.,
int matrix[3][4];is a 3x4 matrix (3 rows, 4 columns). Access viamatrix[i][j]. These are stored in row-major order (行优先存储). You must specify sizes for all but the first dimension when passing to functions. Java’s multi-dimensional arrays are actually arrays of array objects (could be ragged), while C++ multi-dimensional arrays are true multi-dimension in contiguous memory (unless you use pointers to simulate jagged arrays). - Static vs Automatic Arrays: A static local array(静态局部数组) (declared with
staticinside a function) persists for the program’s lifetime and is zero-initialized by default. An automatic array(自动/局部数组) (normal local array) is created each time the function is called and destroyed on exit; it is not automatically initialized (contents are indeterminate if not set). Use static when you need to preserve values between calls or for large arrays to avoid stack usage (but then not thread-safe). - Range-Based for (C++11)(基于范围的for循环) – You can loop through an array easily:
for(int x : arr) { ... }will iterate over each elementxinarr. This is similar to Java’s enhanced for:for(int x : arr). It’s read-only by default (x is a copy). Use reference (for(int &x : arr)) if you want to modify array elements in the loop. - No Built-in Bounds Checking(无边界检查) – Unlike Java, C++ will not throw an error if you go out of bounds on an array; it will just access whatever memory is at that address. This can lead to bugs or security issues. For safer arrays, use
std::array(which has.at(index)that throws an exception on out-of-range) orstd::vector(withatmethod). In critical applications, consider using these or adding manual checks. - C-Strings vs std::string: A C-style string is an array of
charending with'\0'. e.g.,char name[6] = "Alice";(actually 6 elements including null terminator). Manipulating C-strings requires care (functions in<cstring>likestrcpy,strlen).std::string(as covered in Lecture 3) is higher-level and easier to use. When interacting with C APIs, you might convertstd::stringto C-string viac_str()method. - Class Template
std::vector(向量模板类) – A dynamic array provided by the C++ Standard Library (in header<vector>).std::vector<T>represents a resizable array of elements of type T. Key features:- Dynamic sizing(动态大小): It can grow or shrink at runtime. Start with
vector<int> v;and usev.push_back(value);to append elements. The vector manages its own memory (automatically allocates more space as needed). - Access and Capacity(访问与容量): Use
v[index]to access elements (like array, but no bounds check by operator[]), orv.at(index)which throwsstd::out_of_rangeexception if index is invalid. Usev.size()to get current number of elements. Unlike built-in arrays,vectorknows its length. - Initialization: You can initialize with a size:
vector<int> v(10);creates a vector of size 10 (elements default-initialized, e.g., 0 for int). Or initialize with values:vector<int> v = {1,2,3};. - Comparison: Vectors can be compared with
==and!=(element-wise comparison). Two vectors are equal if they have same length and all corresponding elements equal. - Copy and Assignment(拷贝与赋值): You can assign one vector to another (
v2 = v1;) and it copies all elements (深度复制,每个元素都会被拷贝). There is also a copy constructor (e.g.,vector<int> v2(v1);) to initialize from another vector. - Memory Reallocation: Vectors manage an internal capacity. They might allocate more capacity than size for efficiency. When size exceeds capacity, a reallocation happens (which can move the data). Thus pointers/iterators to vector elements may be invalidated when reallocation occurs. (Java’s
ArrayListis similar in this regard.) - Bounds Checking:
vector::atprovides bounds checking. In contrast,operator[]does not check bounds (for performance). Java’sArrayListget(index)does check and throws exception if out of range. - Default initialization: A
vector<int> v(5);will value-initialize the 5 ints to 0, whereas a built-in array of 5 ints (if local) would be uninitialized unless explicitly set. - Iterators: Vectors provide iterators (like pointers) to traverse elements. Example:
for(auto it = v.begin(); it != v.end(); ++it) { cout<< *it; }. (Similar to Java iterators, but in C++ often we use range-for instead for simplicity.) - Advantages:
std::vectoris safer and more flexible than raw arrays. Use it unless you have a specific reason to use C-style arrays.
- Dynamic sizing(动态大小): It can grow or shrink at runtime. Start with
After-class Exercises 课后练习:
-
填空题: 定义数组长度时应使用 ______ 来避免魔术数字(magic number)。【提示】应使用符号常量,例如
const常量或宏。 -
填空题: 用来引用数组特定元素的数字称为该元素的 ______ 。【提示】称为下标或索引(subscript/index)。
-
判断题: 一个数组可以存储多种不同类型的值。(对/错)【提示】**错。**数组中的所有元素类型必须相同。例如,
int数组只能存int,不能混存其它类型。 -
判断题: 数组的下标值通常应使用
float类型,以便表示更大的范围。(对/错)【提示】**错。**数组下标应使用整数类型(如int),浮点数不可直接作为下标(编译时会转换或报错)。 -
判断题: 如果初始化列表中的初始值个数少于数组长度,未提供初始值的元素将被初始化为列表中的最后一个值。(对/错)【提示】**错。*对于不足的部分,C++会将其*初始化为0(数值类型)而不是重复最后一个值。
-
判断题: 如果初始化列表中的值多于数组容量,这将导致编译错误。(对/错)【提示】**对。**初始化时初始值数量不能超过数组声明的大小,否则程序无法通过编译。
-
编程题: 定义一个大小为99的浮点数组
w,写一个程序找出并输出该数组中的最小值和最大值。要求:给出中文解题思路。提示: 可以通过一次遍历数组来同时找出最小值和最大值。具体步骤:先假设数组首元素既是最小值又是最大值,然后从第二个元素开始遍历整个数组,与当前最小值、最大值比较并更新。如伪代码:
minVal = w[0], maxVal = w[0] 对于 i 从 1 到 98: 如果 w[i] < minVal,则 minVal = w[i] 如果 w[i] > maxVal,则 maxVal = w[i] 输出 minVal 和 maxVal这样单次循环即可完成查找(时间复杂度O(n))。需要注意数组必须已填入有效的数值才能进行比较(未初始化元素不能直接比较)。在实现中,先对
w赋值(可通过输入或随机生成等),然后应用上述逻辑得到结果。请确保访问数组时索引不越界(0到98)。(The approach is similar in Java: one would use a loop from 0 to length-1, updating min and max accordingly.)
Lecture 7: Pointers and Memory Management (第7讲:指针与内存管理)
Key Concepts 主要知识点:
- Pointer(指针) – A variable that holds a memory address(内存地址) as its value. If a variable
xresides at some address in memory, a pointer can “point to”xby storing that address. In C++, pointers are declared with an asterisk*. For example,int *p;declarespas a pointer to int (能够存放某个整型变量的地址). Pointers provide indirection(间接引用): they allow access to the object they point to. (Java没有显式的指针类型;Java的引用在某种程度上类似指针,但不能进行算术运算或转换为整数地址。) - Address-of Operator
&(取地址运算符) – A unary operator that yields the memory address of its operand. For example, ifint y = 5;then&ygives the address whereyis stored. You can assign that to a pointer:int *yPtr = &y;nowyPtrcontains y’s address. Think of&as “give me the location of”. - Dereference Operator
\*(解引用/间接运算符) – A unary operator used on a pointer to access the object it points to. For example, ifint *p = &y;then*prefers to the same variable asy. So*p = 10;would setyto 10.*here is called the indirection operator(间接访问运算符), and using*pis dereferencing the pointer(解引用指针). Important:*has two roles in C++: declaration (to denote a pointer type) and dereferencing. The context differentiates them. (Java does not allow direct pointer dereferencing; object references are automatically dereferenced with.operator to access fields/methods.) - Pointer Declaration and Initialization(指针声明与初始化) – When declaring multiple pointers in one statement, note that the
*applies to each variable individually: e.g.,int *p, q;declarespas pointer to int andqas int (not a pointer). It’s clearer to separate declarations. Always initialize pointers(初始化指针) when declaring if possible, either to a valid address or tonullptr(C++11) orNULL(older) or0. An uninitialized pointer contains an indeterminate address (野指针), dereferencing it causes undefined behavior. Setting a pointer to null pointer(空指针) indicates it points to nothing (a safe “nowhere” value). In C++NULLis typically defined as 0; C++11 introducednullptras a type-safe keyword for null pointers. - Null Pointer(空指针) – A pointer that is not pointing to any object or function. In C++ you can assign
0orNULLto a pointer to make it null. Dereferencing a null pointer (*pwhenpis null) is invalid and will likely crash the program (访问空指针会导致运行时错误). Always checkif(p != nullptr)(orif(p)since null is falsey) before dereferencing if unsure. (Java references throwNullPointerExceptionif used when null; in C++ it’s just undefined behavior with no automatic check). - Pointer Arithmetic(指针算术) – Pointers can be incremented or decremented (if they point to elements of an array). For example, if
pis anint*,p++advancespto point to the next integer in memory (地址增加sizeof(int)字节). Likewisep + nmoves the pointer forward by n elements. Pointer arithmetic is scaled by the pointed type size. So adding 1 to anint*moves 4 bytes (on typical 32-bit int system), adding 1 to adouble*moves 8 bytes, etc. This is how C-style arrays and pointers are closely related (see next point). (Java does not support pointer arithmetic at all. All array indexing in Java is done with the index and internal checks, not by moving pointers.) - Arrays and Pointers Relationship(数组与指针的关系) – In C++, the name of an array can decay to a pointer to its first element. For example, if
int a[10];, thena(in most expressions) is treated asint*pointing toa[0]. So you can doint *p = a;and then*(p+3)is equivalent toa[3]. Also,a[i]is defined as*(a + i). There is a close interplay: pointer arithmetic + dereference can simulate array indexing. Note: Arrays allocated withnew(likeint *p = new int[10];) should be accessed with pointer notation*(p+i)or as ifpwere array namep[i]. One key difference: a pointer is a variable (you can reassign it), but an array name is not a modifiable lvalue (不能给数组名赋新地址值). - Pointer vs Reference(指针与引用) – C++ also has references(引用), declared with
&(e.g.,int &ref = x;). A reference is an alias to an existing variable; once initialized to refer to a variable, it cannot be changed to refer to another. References are simpler (no deref operator needed: userefdirectly as alias), but they cannot be null and must be initialized upon creation. Pointers, on the other hand, can be reassigned, can be null, and require explicit dereferencing. Both can be used for pass-by-reference to functions, but references are generally safer and preferred for that purpose. (Java’s object variables are more like pointers “under the hood,” but Java doesn’t allow pointer arithmetic or direct memory access. Java’s references are automatically dereferenced; you don’t see an explicit\*as in C++.) - Dynamic Memory Allocation(动态内存分配) – Using
newto allocate memory on the heap. For example,int *p = new int;allocates anintand returns a pointer to it. For arrays:int *arr = new int[50];allocates an array of 50 ints on heap. Complement withdeleteto free a single object (delete p;) ordelete[]for arrays (delete[] arr;) to avoid memory leaks (内存泄漏). Ifdeleteis not called, the allocated memory remains occupied until program ends. Java doesn’t have explicitnew/deletepairing; it relies on garbage collector to reclaim unused objects. - Memory Leaks and Dangling Pointers(内存泄漏与悬空指针) – A memory leak(内存泄漏) occurs if heap memory is allocated and never freed (在C++中忘记调用
delete就丢失了指针). Over time, leaks exhaust memory. A dangling pointer(悬空指针) is a pointer that refers to memory that has been freed or is otherwise invalid. For example, if youdelete p;and then still usep(without resetting it) – it’s pointing to deallocated memory. Accessing it is undefined behavior. Best practice: afterdelete p;setp = nullptr;to avoid accidental use. Smart pointers from<memory>(likestd::unique_ptr) help manage dynamic memory automatically (RAII idiom). - Pointer to Constant / Constant Pointer(常量指针/指向常量) – C++ allows const qualifiers with pointers:
int * const pis a constant pointer to int (指针自身是常量,不能改指向其他地址,但所指对象可变)。const int * p(orint const * p) is a pointer to a const int (指向常量的指针,不能通过*p修改对象,但指针本身可变)。const int * const pis a const pointer to a const int (指针和所指对象都不可修改). These add compile-time safety (e.g., if a function shouldn’t modify what it points to, takeconst T*). Java doesn’t have an equivalent; final references in Java mean you can’t reassign the reference, but you can still modify the object’s contents if it’s mutable.
- Function Pointers(函数指针) – A pointer can also point to a function’s address. Declaration syntax:
int (*fp)(int, int) = &functionName;. This is advanced but allows passing functions as arguments (similar to delegates or lambdas in higher-level terms; C++11 has lambdas which are often easier). (Java prior to 8 didn’t have function pointers; Java 8 introduced lambda expressions and method references to achieve similar functionality.) - Pointer Safety Enhancements (C++11) – Use
nullptrinstead ofNULLfor clarity and type safety. Use smart pointers (std::unique_ptr,std::shared_ptr) to automatically managedelete. These are part of modern C++ best practices to reduce manual memory errors.
After-class Exercises 课后练习:
- 填空题: 一元运算符
&称为取地址运算符,用于获取其操作数的 ______ 。【提示】获取变量的内存地址。 - 填空题: 一元运算符
*用于指针时称为间接运算符或解引用运算符,用于获取指针所指向的 ______ 。【提示】获取指针指向的目标对象/值。 - 填空题: 可以直接赋给指针变量的唯一整数值是 ______ ,表示该指针不指向任何有效对象。【提示】整数0(即空指针
NULL/nullptr)。 - 判断题: 在C++中,指针可以指向不同类型的变量,不需要类型匹配。(对/错)【提示】**错。**指针有类型,必须与所指对象类型一致或兼容(需要时可强制转换,但一般不安全)。例如,
int*只能指向int或可转换为int的地址,不能直接指向double。 - 判断题: 指针与数组紧密相关,数组名在大多数表达式中会转换为指向其首元素的指针。(对/错)【提示】**对。**数组名可视为指向数组首元素的地址,如
a可以作为&a[0]使用。因此*(a+1)与a[1]等价。 - 判断题: 使用
new运算符分配的内存必须使用delete释放,否则会发生内存泄漏。(对/错)【提示】**对。**每次new都应有相应的delete,否则分配的堆内存无法回收,导致内存泄漏。可使用智能指针防止忘记释放。 - 判断题: C++引用(reference)类似指针,但引用一旦绑定某对象后不可更改,且不能为NULL。(对/错)【提示】**对。**引用是对象的别名,必须初始化,一经绑定无法指向别的对象,且不存在“空引用”。指针则可以在任何时候指向其他对象或设为NULL。
- 判断题: 将指针加1实际上使其指向下一个内存地址。(对/错)【提示】错(有细微差别)。指针加1后指向下一个同类型元素的位置,不一定是下一个字节地址,而是跨过当前类型大小的字节数。例如
int*加1跳过4字节到下一个int地址。
Lecture 8: Classes – Deeper Look (Part 1 & 2) (第8讲:更深入的类机制 上、下)
Key Concepts 主要知识点:
-
Separate Header and Implementation(头文件与实现分离) – It is common to split a class into a header file(头文件) (
.hor.hpp) and an implementation file(实现文件) (.cpp). The header contains the class definition (class declaration, with member function prototypes), and the implementation file contains the function definitions (函数定义) of those member functions. This separation aids maintainability: changes in implementation don’t force recompilation of code that includes the header (as long as interface remains unchanged). To use a class in multiple source files, you include the header. For example,#include "GradeBook.h"in any file usingGradeBookclass. (Java没有头文件—接口和实现都在.java文件内; 但Java可以通过interface和implementation classes来分离定义。C++头/源分离更贴近编译过程需求。) -
Default and Value Parameters in Constructors(构造函数默认参数) – Constructors (and any function) in C++ can have default arguments(默认参数). This allows one constructor to handle multiple cases. For instance:
class Player { public: Player(string name = "Anonymous", int level = 1) { this->name = name; this->level = level; } };With this, you can construct
Player p1;(uses defaults "Anonymous",1), orPlayer p2("Alice");(level defaults to 1), orPlayer p3("Bob",5);. At most one default constructor exists per class (构造函数默认为一个,因为参数默认可组合成无参调用),which is one that can be called with no arguments. If you provide default values for all parameters of a constructor, that constructor becomes the default constructor. Java doesn’t support default parameters; you must overload constructors instead. -
Destructor(析构函数) – A special member function with the same name as the class prefixed by
~(如~Player()), called automatically when an object is destroyed (对象生命周期结束时调用). The destructor’s role is to free resources that the object may have acquired (like memory, file handles) before the object is removed from memory. Key properties:- A class has at most one destructor (no overloading for destructors, no parameters, no return type). If you don’t define one, the compiler generates a default destructor that simply calls destructors of data members (for class members) and does nothing special.
- Destructors are called automatically in the reverse order of construction (构造相反顺序) when objects go out of scope or are explicitly deleted. For example, in stack unwinding, local objects are destroyed in the opposite order of their creation.
- If an object is created with
new, its destructor is called when youdeletethat object. If created as a local (stack) object, destructor called at end of scope. For global/static objects, destructors called when program terminates (in unspecified order across translation units). - If
exit()orabort()is called, destructors of some objects may not execute (especiallyabort()kills program without cleanup). - Important for memory management: If your class allocates dynamic memory (via
new), define a destructor todeletethat memory to avoid leaks (or better use smart pointers to manage automatically). In classes that follow RAII (Resource Acquisition Is Initialization), the destructor releases the resource. Example: a file wrapper class closes the file in its destructor. (Java has no direct destructor concept; Java uses garbage collection. For cleanup (like closing files), Java relies onfinallyblocks or try-with-resources, or overridingfinalize()(which is deprecated in Java 9+). Finalize is not deterministic and not recommended. In contrast, C++ destructors are deterministic and key to resource management.
-
Constructor and Destructor Call Order(构造和析构的顺序) – For automatic objects (栈对象), constructors execute when entering their scope, destructors when leaving scope (like end of function or block). When multiple objects exist:
- If objects are defined in sequence, they are constructed in that sequence and destructed in reverse sequence (后创建的先析构).
- For class composition (对象成员): Member objects are constructed before the enclosing class’s constructor body executes (in order of their declaration in the class) and destructed after the class’s destructor body executes (in reverse order).
- For inheritance: Base class constructor runs before derived class constructor, and base destructor runs after derived destructor (base part is last constructed, first destroyed since derived “contains” base).
- Global and static objects: constructed before
mainbegins (order across translation units not strictly defined), destructed aftermainexits (order is reverse of construction within each translation unit). - If
exit()is used, static objects might not destruct properly; ifabort()used, none will. - In summary: 构造函数调用按声明/继承顺序向内深入,析构函数调用按相反顺序向外展开。
-
Const Objects and const Member Functions(常量对象与常量成员函数) – If you declare an object as
const(如const Player p;), it cannot be modified. In particular, you can only call that object’s const member functions(常量成员函数). A member function is made const by addingconstafter its parameter list, both in prototype and definition. For example,class Player { public: string getName() const { return name; } void setName(string n) { name = n; } private: string name; };Here
getName()is a const member function – it promises not to modify the object (编译器会强制保证函数体内不修改成员变量). You can callgetName()on a const Player, but cannot callsetName()because that function isn’t marked const. Const correctness helps catch unintended modifications and allows usage of objects in read-only context (like passing a const reference to a function to ensure it doesn’t modify the argument). Java does not have const methods or const objects. Instead, immutability in Java is a design choice (like making fields private and not providing setters, or usingfinalfor fields, but there’s no language-enforced const for methods or objects). C++ const is a compile-time check to prevent modification through that interface. -
Composition(组合) – A class can have members that are objects of other classes (class A has a member of class B type). This is called a “has-a” relationship or composition (合成关系:一个类拥有另一个类的对象). For example, a
Carclass might have anEngineobject as a member. Composition implies that the lifetime of the part (成员对象) is tied to the whole: when the Car is constructed, its Engine member is constructed; when Car is destroyed, Engine is destroyed. It’s a way to reuse classes by building complex ones from simpler ones (Car由Engine, Wheel等部件组成). (Java also has object references as fields to achieve composition, with similar semantics aside from memory allocation differences. In Java, an object field is a reference that could be null or refer to a shared object; in C++ if you have an object member (not pointer), it’s embedded and cannot be null – it always exists as part of the containing object.) -
Friend Functions and Friend Classes(友元函数与友元类) – The
friendkeyword allows a non-member function or another class to access the private/protected members of a class. This is an exception to the usual encapsulation rules. For example:class MyClass { friend void secretFunction(MyClass& obj); friend class MyFriend; private: int secretData; };Here
secretFunctionand all members ofMyFriendclass can accessMyClass::secretDatadirectly. Friendship is granted, not taken: it must be declared within the class that is giving access. It’s neither symmetric nor transitive: if A is friend of B, B is not automatically friend of A; if A is friend of B and B is friend of C, A is not friend of C. Use cases: sometimes needed for operator overloading (to allow an external function to access privates) or if two classes need intimate access to each other’s internals. But overuse offriendcan break encapsulation and make maintenance harder, so use sparingly. (Java has no friend concept. Java achieves similar trust through package-private access (classes in the same package can access each other’s package-private members) or simply making members public. But there's no direct way to allow one specific external function/class access without making things globally accessible, short of nested classes. C++ friend is a targeted relaxation of access control.) -
thisPointer(this指针) – Inside a non-static member function,thisis a pointer to the object on which the function was invoked. It has typeClassName* const(a const pointer to the object). You can usethisto refer to the object’s members (especially if names are shadowed) or to return the object itself (like in operator overloading chaining). In a const member function,thisis of typeconst ClassName*. Java similarly has athisreference inside instance methods, though it’s used less explicitly (commonly only for disambiguating field names or passing the current object as parameter). In C++ you often see patterns likereturn *this;in methods to allow chaining. -
Static Class Members(静态类成员) – C++ classes can have static data members(静态数据成员) and static member functions(静态成员函数). A static data member is essentially a global variable that is scoped to the class (所有对象共享的类级变量). It exists independently of any object instances (即使没有创建对象,静态成员也存在并可访问). You define it outside the class as well (one definition in a .cpp): e.g.,
int MyClass::count = 0;to allocate it. Use cases: class-wide information, like a counter of how many objects have been created (each constructor increments, destructor decrements). Static member functions are functions not tied to a specific object (nothispointer inside). They can only access static members (because there’s no object context). Call static members with class name:MyClass::countorMyClass::staticFunc(). (Java’sstaticfields and methods are analogous. The main difference: C++ static data members need to be defined in one .cpp file, whereas Java static fields are defined in the class itself at declaration. Also, C++ has no direct equivalent of Java’s static initializer blocks, but you can assign initial values in the definition or use static constructors in modern C++17 (inline static initializations).)- If you want to call a static function or use a static data without any objects, you can do so. E.g., a class
MathUtilscould havestatic double PI; static double square(double x);and you useMathUtils::PIorMathUtils::square(5.0)directly (like Java’sMath.PIandMath.sqrt()). - Static data members are initialized to 0 (for fundamental types) by default if not explicitly initialized. They live in static storage (like global variables).
- A common pattern is singleton: a class with a private static instance of itself and a static method to get that instance.
- Remember to define static data in one translation unit or mark it
inline(C++17) to avoid multiple definitions error.
- If you want to call a static function or use a static data without any objects, you can do so. E.g., a class
-
Member Initializer List(成员初始值列表) – A syntax used in constructor definitions to initialize data members (especially important for const and reference members which must be initialized, not assigned). For example:
class Example { const int size; int * data; public: Example(int s): size(s), data(new int[s]) { /*...*/ } };The part
: size(s), data(new int[s])is the initializer list. It initializessizeanddatabefore the constructor body runs. Benefits:- Efficiency: avoid default-constructing then assigning. For complex members, initialization can be faster than default construct + assignment (especially for objects of classes as members).
- Necessity: const and reference members cannot be assigned in constructor body; they must be initialized in the initializer list. Static data members cannot be initialized in the initializer list (they’re handled separately at definition).
- Order: Members are initialized in the order of their declaration in the class, not the order in the initializer list. It’s wise to list them in same order to avoid confusion. (Java has no equivalent because instance fields can be assigned in body or an init block, but effectively Java does initialization before constructor body too, except you don’t control the order beyond writing them in code order. Java final fields must be assigned by constructor completion but you usually just do it in constructor body or inline initializers, no special syntax needed.)
After-class Exercises 课后练习:
- 填空题: 若要允许一个非成员函数访问类的私有成员,可以在类定义中将该函数声明为 ______ 函数。【提示】声明为该类的**友元(friend)**函数。
- 填空题: 对象的非静态成员函数可以通过一个隐含的“自我指针”访问所属对象,该指针称为 ______ 指针。【提示】称为
this指针。
(Lecture 8 slides did not explicitly list many exercise questions beyond friend and this pointer fill-in, so we focus on those.)
Suggested Answers 答案: 1) 友元函数; 2) this指针。请注意,friend关键字可以使指定的函数或类突破封装直接访问私有成员,但应谨慎使用。this指针在C++中是隐含参数,用于指向调用该成员函数的对象地址,在需要时可显式使用,如返回*this实现链式调用。
Lecture 9: Operator Overloading (第9讲:运算符重载)
Key Concepts 主要知识点:
-
Operator Overloading(运算符重载) – C++ allows giving operators like
+,-,==,<<, etc., custom meanings for user-defined types (为用户自定义类型定义运算符功能). This enhances expressiveness – e.g., you can usea + bfor two complex numbers or concatenate two strings with+if you define it. In C++, most operators can be overloaded except a few (notably.member access,.*pointer-to-member,::scope resolution,?:ternary, andsizeofcannot be overloaded). To overload an operator, you define a function with special nameoperatorXwhere X is the operator symbol. For example:class Complex { public: Complex operator+(const Complex& rhs) const { return Complex(real + rhs.real, imag + rhs.imag); } };This overloads
+for Complex addition. Thenc1 + c2calls this function (as a member function onc1with argumentc2). Some operators are best overloaded as member functions (like assignmentoperator=must be member) and some as friend or free functions (likeoperator<<for streams, since left operand isn’t a class object but anostream).-
Syntax: If a binary operator is overloaded as a member, it takes one parameter (the right-hand side). If as a free function, it takes two parameters (left and right). For symmetric operations, free function can be friend for access.
-
Rules: You cannot change operator precedence or associativity, and you cannot create new operators – only overload existing ones. The overloaded operator’s arity (unary/binary) and meaning should be intuitive.
-
Example: Overloading
<<for output:std::ostream& operator<<(std::ostream& os, const Complex& c) { return os << c.real << "+" << c.imag << "i"; }This allows
cout << complexObj;to print a Complex. Usuallyoperator<<is a non-member (cannot be member of std::ostream or Complex), hence made a friend of Complex to access privates. -
Operator=: If you define custom copy semantics, overload assignment operator. If not, C++ provides a default member-wise copy assignment. Similarly, you can overload
operator==to compare objects meaningfully (default does bitwise compare? Actually default==not generated, must overload for user types).
-
-
Conversion Operators(类型转换运算符) – You can define
operator type()in a class to convert an object to another type implicitly. E.g.,operator int() const { return value; }in a class would allow an object to be used where int is expected. Use carefully – implicit conversions can introduce ambiguities or unexpected results. (In Java, you can’t overload casting or define implicit conversions; you must use explicit methods). -
Explicit Keyword – In C++11 and above, you can mark single-parameter constructors as
explicitto avoid them being used as implicit conversions. Similarly, conversion operators can be marked explicit (C++20) to require explicit cast. This prevents unintended conversions. (Java doesn’t have an equivalent since no user-defined implicit conversions exist.) -
Overloading vs Overriding – Operator overloading is about same operator symbol working on different types (ad-hoc polymorphism). It’s resolved at compile time by checking operand types. This is different from overriding virtual functions (runtime polymorphism, Lecture 11). (Java doesn’t have operator overloading at all, so less confusion there; C++ needs careful distinction between overload vs override – hence
overridekeyword introduced for clarity in virtual functions override).
After-class Exercises 课后练习:
- 填空题: 要重载一元或二元运算符,需要定义一个名为 ______ 的函数。【提示】函数名形式为
operator+、operator==等,例如重载+则函数名为operator+。 - 判断题: 运算符重载可以改变运算符的优先级从而影响表达式求值顺序。(对/错)【提示】**错。**重载运算符不会改变其既定的优先级和结合性,表达式求值规则保持不变。
- 判断题: Java允许用户为自定义类重载运算符。(对/错)【提示】**错。**Java不支持运算符重载,除了
+用于字符串连接是语言内置特例外,其他运算符不可重定义。
Answers 答案: 1) 形如operator+的函数名(以operator加运算符符号命名); 2) 错;3) 错。请记住,C++运算符重载应遵循直观原则,例如operator+应实现类似“加法”的语义。不能重载的新运算符包括.、::、?:等。而在Java中运算符重载不可用,只能通过定义方法实现类似功能(例如用add()方法代替重载+号)。
Lecture 10: Inheritance (Extending Classes) (第10讲:继承 – 扩展类)
Key Concepts 主要知识点:
-
Inheritance(继承) – A mechanism for creating a new class (子类/派生类) from an existing class (基类/父类), absorbing its attributes and behaviors and optionally adding new features or overriding existing ones. In C++, class inheritance is specified with a colon and access specifier. Example:
class Student : public Person { ... };means Student inherits publicly from Person (Student is-a Person). Key points:- The derived class automatically has the data members and member functions of the base class (though private members of base are not accessible directly in derived, they are present but hidden). Public and protected base members become part of the derived class interface (public stays public, protected stays protected if using public inheritance).
- Access specifier (public/protected/private) in inheritance: This determines how the base’s public/protected members are treated in derived.
- Public Inheritance 公有继承: The most common form. Base class’s public members remain public in derived, protected remain protected. This models an “is-a” relationship: a Student is a Person, so it should be usable wherever a Person is expected (Liskov Substitution Principle).
- Protected Inheritance 受保护继承: Base public and protected become protected in derived. Not common; essentially, you don’t want outside code to treat the derived as the base type.
- Private Inheritance 私有继承: Base public and protected become private in derived. It’s more of a composition (“implemented in terms of”) than an is-a; not polymorphic. Rarely used except in certain design patterns or when you want to hide base interface.
(Java only has a concept similar to public inheritance. All Java classes extend a base (explicit or
Objectimplicitly), and all public/protected remain accessible in subclass. Java doesn’t allow changing the access level of inherited members except you cannot reduce visibility of an overridden method.)
- Single vs Multiple Inheritance: C++ supports multiple inheritance (一个子类可同时继承多个基类). Syntax:
class C : public A, public B { ... };. This can lead to complexities like diamond problem if both A and B inherit from a common base (C++ resolves with virtual inheritance if needed). Java forbids multiple class inheritance; it allows multiple interfaces instead (interfaces are like pure abstract base classes). - Inheritance Hierarchies(继承层次): Classes can be derived multiple levels (子类的子类, etc.). At the root, often an abstract base class (see polymorphism in Lecture 11).
- Use inheritance when there is a clear generalization-specialization relationship (一般和特殊的关系). For example,
Personas base,StudentandTeacheras derived, sharing common attributes of Person but adding their own.
-
Protected Members(保护成员) – In C++,
protectedaccess means the member is accessible within the class and its derived classes (and also within the same module as friend or within same class scope). Protected is a way to allow subclasses to use base internals but keep them hidden from outside world. In Java,protectedmeans accessible to subclasses and also to classes in the same package. C++ has no package concept, so protected is strictly subclass access (plus friend). Protected is useful when you want an inheritor to have some degree of access for convenience, but you don’t want the general user (via a base class reference) to access those members. -
Base and Derived Constructors(基类与派生类构造) – When you instantiate a derived class object, the base class constructor runs first to initialize the base part of the object, then the derived class’s constructor runs. The derived constructor can call a specific base class constructor using an initializer list:
class Student : public Person { public: Student(string name, int gradYear) : Person(name), graduationYear(gradYear) { } ... };If a base constructor requires arguments, you must call it explicitly in the initializer list. If you don’t call any, C++ tries to call the base’s default constructor implicitly. Similarly, destructors execute in reverse: first the derived’s destructor, then the base’s destructor. This ensures proper construction/destruction chain. (Java automatically calls the base class constructor (no syntax for choosing base constructor except via
super()call as first line in subclass constructor). If you don’t callsuper(...)explicitly, Java inserts a call to the no-arg super constructor. C++ similarly calls default base ctor if not specified.) -
Overriding and Hiding(重写与隐藏) – If a derived class defines a method with the same signature as a base class virtual method, it overrides(重写) that method (when called through a base pointer/reference, the derived version is used at runtime – see Lecture 11 on polymorphism). If the base method is not virtual (just a normal function), then defining a same name in derived results in hiding (also called shadowing) – which is not polymorphic; it simply means if you call that function on a derived object (statically typed as derived), you get the derived version, but if you call it on base or through a base pointer, you get base version because static binding. To override properly, the base function should be virtual.
- C++11 introduced the
overridekeyword to explicitly mark overriding functions; the compiler will error if it doesn’t actually override a base virtual. Use this to avoid mistakes (accidental signature mismatch). Java by default treats all non-final instance methods as virtual and overrides them if signatures match in subclass. Java’s@Overrideannotation is like C++overrideto catch errors. - Also note: In C++, if you overload a function name in derived with a different signature, it hides all base class functions of the same name (even those with different signatures) from the scope. You can bring them in with
using Base::functionName;if needed. Java doesn’t have this exact issue because name resolution is always at compile-time by full signature, and methods with different sig are simply overloads existing in class. C++ name hiding is a quirk due to separate compilation and might requireusingto fix.
- C++11 introduced the
-
Multiple Inheritance and Virtual Base Classes(多重继承与虚基类) – If a class inherits from two classes that have a common base, the default is you get two copies of that base in the derived object (ambiguity which one to use). C++ allows virtual inheritance to solve the diamond problem, so the common base is shared (one subobject). Syntax:
class MyClass : virtual public BaseClass { ... };. Virtual inheritance ensures only one base subobject even if base appears multiple times in hierarchy. This is advanced and only needed in certain MI scenarios. Java’s way to avoid diamond is by not allowing multiple inheritance of classes at all (interfaces don’t have state so diamond doesn’t cause state duplication). -
Polymorphism preview – (Though more in Lecture 11) Under inheritance, a derived object can be treated as a base object. You can assign a
Derived*to aBase*(implicit upcast), or call a function expecting a Base reference with a Derived object. This is core to substitutability. If the function is virtual, the derived override will execute (if polymorphism is set up). If not virtual, base version executes (static binding). Non-polymorphic inheritance is essentially reuse of code without runtime flexibility.
After-class Exercises 课后练习:
- 判断题: 在公有继承下,基类的
public成员在派生类中变为private。(对/错)【提示】**错。**公有继承不会降低可见性,基类public成员在派生类中仍为public;protected成员在派生类中仍为protected。只有private继承才会将基类的public和protected成员变为private。 - 判断题: C++允许一个类同时继承自多个基类。(对/错)【提示】**对。**C++支持多重继承,一个派生类可有多个直接基类。但要注意可能出现“菱形继承”问题,需要虚继承解决。Java不支持类的多继承。
- 判断题: 可以使用基类类型的指针或引用指向派生类对象。(对/错)【提示】对。**派生类对象**是一种基类对象,因此基类指针/引用可指向派生对象(向上转型),这实现了多态的基础。通过基类指针调用虚函数会执行派生类覆写的版本(见Lecture 11)。
Answers 答案: 1) 错;2) 对;3) 对。请注意,在使用基类指针指向派生对象时,若调用非虚函数,则只会执行基类实现(静态绑定);只有虚函数才会多态地调用派生实现。良好的面向对象设计在继承层次中运用public继承表示”is-a”关系,并尽量避免使用protected或private继承。此外,尽管C++允许多继承,但设计时需谨慎,Java通过接口解决多继承需求,这在C++中也可以用组合(composition)或虚继承达到类似目的。
Lecture 11: Polymorphism and Virtual Functions (第11讲:多态与虚函数)
Key Concepts 主要知识点:
-
Polymorphism(多态) – Literally "many forms". In OOP, it refers to the ability to treat objects of different derived classes through a common base class interface, and have the correct overridden functions called according to the actual object type at runtime. Polymorphism enables you to “program in the general rather than the specific”(面向抽象编程,而非面向具体实现). The primary mechanism for polymorphism in C++ is virtual functions(虚函数).
- Polymorphism lets you write code that works on base class pointers/references but actually processes derived class objects. For example, you might have a
Shapebase class with a virtualdraw()method, and derived classesCircle,Squareoverridedraw(). If you store various shapes in an array ofShape*and calldraw()on each, the appropriate derived class implementation executes (circle draws a circle, square draws a square). - This relies on late binding (动态绑定): the decision of which function body to execute is made at runtime based on the actual object type, not at compile time (静态类型是基类,但对象实际类型是子类). Without virtual, C++ would do early binding (静态绑定) and call the base version always when using base pointer.
- Polymorphism usually goes hand in hand with inheritance(继承) and virtual functions.
- Polymorphism lets you write code that works on base class pointers/references but actually processes derived class objects. For example, you might have a
-
Virtual Functions(虚函数) – Declared with keyword
virtualin the base class, these functions are intended to be overridden in derived classes. A virtual function call is resolved dynamically at runtime via the vtable(虚函数表) mechanism. For example:class Base { public: virtual void foo() { cout<<"Base"; } }; class Derived : public Base { public: void foo() override { cout<<"Derived"; } };Now,
Base* p = new Derived(); p->foo();will print "Derived" becausefoois virtual. Iffoowere not virtual, it would print "Base" (static binding).- To override a virtual function in C++, the signature must match exactly (aside from
overrideor covariant return). Marking the override withoverride(optional but recommended C++11) helps catch errors. - Pure Virtual Functions(纯虚函数):
virtual void foo() = 0;in a class makes it abstract – no implementation in base, derived classes must override it to be concrete. A class with any pure virtual is an abstract class(抽象类), cannot be instantiated. This is similar to Java’s abstract methods and abstract classes. Pure virtual functions allow defining an interface that derived classes must implement. - Virtual functions can be overridden to provide specialized behavior in subclasses. If a subclass does not override a virtual, it inherits base’s implementation. You can call base class version from derived if needed (explicitly
Base::foo();). - If a derived class fails to override a pure virtual, that derived remains abstract. All concrete (非抽象) derived classes must override all base class pure virtuals.
- Virtual Destructor: If a class is meant to be a base class (especially if delete via base pointer is possible), declare a virtual destructor. This ensures that deleting a derived object through a base pointer calls the derived’s destructor fully. In polymorphic base classes, the destructor should be virtual to avoid resource leaks. (Java has GC, so no concept of destructors; all objects properly finalize when collected. But if Java had manual deletion, their Object class’s finalize is akin but not the same. C++ requires explicit management).
- To override a virtual function in C++, the signature must match exactly (aside from
-
Dynamic Binding and Vtable(动态绑定与虚函数表) – At runtime, polymorphic objects carry a pointer to a vtable (a table of function pointers for virtual functions). When you call a virtual function, it uses the vtable of the actual object’s class to jump to the right function. This indirection is why calls through base pointers go to derived overrides. Implementation detail: typically the first element of an object of a class with virtual functions is a vptr (虚指针) pointing to the table. (Java’s method dispatch is similar in concept; all non-static, non-final methods in Java use a vtable-like mechanism for dynamic dispatch). The cost is one pointer dereference; negligible in most cases.
-
Polymorphic Interface and Extensibility – Polymorphism allows writing code that doesn’t need to know exact subclasses in advance. For example, a function
printShapeArea(Shape& s)can calls.area()and get correct result for Circle, Square, etc., without separate code for each. Adding new Shape types (Triangle, etc.) doesn’t require changingprintShapeArea– it’s extensible(易扩展). This is the Open-Closed Principle: modules open for extension (via new subclasses) but closed for modification. Polymorphism in C++ and Java achieves that. -
Upcasting and Downcasting(向上转型与向下转型) – Upcasting is converting a derived object reference/pointer to a base type (安全且隐式进行). This is how we store derived in base pointer for polymorphism. Downcasting is converting base reference/pointer to a derived type. In C++, use
dynamic_cast<Derived*>(basePtr)for a safe downcast (it returns nullptr if object isn’t actually aDerived). This requires the base class to have at least one virtual function (so it has RTTI info). In Java, downcast uses(Derived) baseRef, which throwsClassCastExceptionif the object isn’t that type. The logic is similar: check actual type at runtime. If downcasting, ensure the object is indeed that subclass (viadynamic_castor other means). Minimizing downcasts is often a design goal (rather use polymorphic virtual calls).typeidand RTTI: C++ providestypeidto get type info at runtime (for polymorphic types yields actual type). Java hasinstanceoforgetClass().- If you find you need to downcast often, consider if virtual functions could handle the variation instead (polymorphism intended to reduce explicit type checking).
-
Abstract Base Classes(抽象基类) – A class with one or more pure virtual functions. It serves as an interface (perhaps partial implementation) that cannot be instantiated. Only concrete subclasses (classes implementing all pure virtuals) can be instantiated. Abstract classes in C++ correspond to abstract classes in Java (with abstract methods). They allow defining interfaces. For example, an abstract
Shapewith pure virtualdraw()andarea(), implemented by concreteCircle,Rectangle, etc. Attempting to create aShapeobject would be a compile error (just likenew Shape()illegal in Java if Shape is abstract). -
Virtual Inheritance of Destructors – Emphasizing: if a class is meant to be base class (polymorphic), declare virtual destructor. If not, deleting derived via base pointer is undefined (the base destructor will run but not the derived’s portion). Polymorphic base classes typically have at least one virtual, so destructor should be virtual as well. (Java doesn’t have this issue because all objects are tracked by GC; you don’t manually delete and all parts get collected.)
After-class Exercises 课后练习:
- 判断题: 若基类的成员函数被声明为虚函数,则通过基类指针调用派生类对象的该函数时,会执行派生类版本。(对/错)【提示】**对。**虚函数调用会在运行时绑定到对象实际类型的实现。如果没有将其声明为virtual,则不会发生多态。
- 判断题: 含有纯虚函数的类是抽象类,不能直接创建对象。(对/错)【提示】**对。**抽象类必须被继承并实现纯虚函数才能实例化对象。
- 判断题: 通过基类指针删除派生类对象时,应确保基类析构函数为虚函数,否则可能导致资源未正确释放。(对/错)【提示】**对。**基类析构应为virtual,以便delete时调用派生类析构函数。否则行为未定义,通常会导致只执行基类析构,派生部分没释放,造成内存泄漏或错误。
- 填空题: 要求所有派生类必须覆写某函数,可将该函数在基类中声明为 ______ 。【提示】将函数定义为纯虚函数(pure virtual),语法是在函数声明末尾加
=0。 - 填空题: C++中,将
Base*指针安全地转换为指向其派生类型对象的指针,应该使用的运算符是 ______ 。【提示】使用运行时类型识别的dynamic_cast。例如dynamic_cast<Derived*>(basePtr)。
参考答案: 1) 对; 2) 对; 3) 对; 4) 纯虚函数; 5) dynamic_cast。以上强调:多态让我们可以通过基类接口操作不同派生类对象,并自动调用恰当的实现。在设计类层次时,基类应尽可能将接口函数声明为virtual,并在需要强制子类实现时使用纯虚函数将类抽象化。另一方面,如果类不打算作为基类使用,则无需将析构函数声明为虚的,这样略微减少虚表开销。Java程序员转到C++需特别注意虚函数的声明和override关键字的使用,以避免逻辑错误。另外,在需要判断对象实际类型时,C++的dynamic_cast相当于Java的强制类型转换加instanceof检查,它返回nullptr表示类型不匹配,而Java则抛出异常。通过合理使用多态,可极大提高代码的扩展性和可维护性。