C++基础问题
掌握形参默认带缺省值的函数
函数调用时
#include <iostream>
int sum(int a, int b = 20) {
return a + b;
}
int main() {
int a = 10, b = 20;
int ret = sum(a, b);
cout << "ret: " << ret << endl;
ret = sum(a);
/*
a 使用默认值
压栈: 时压入 a 的值和 20.
push 14H
mov ecx, dowrd ptr[ebp - 4]
push ecx
call sum
*/
ret = sum();
// 压入 10 和 20.
}
总的来说,函数效率有所增长:减少一次 push 指令。
带有缺省的函数的声明
// 一个缺省只能被声明一次,并且只能被从右向左声明
#if 1 // 编译通过
int sum(int a = 10, int b = 20);
#elif // 报错: 默认值只能给一次
int sum(int a = 10, int b = 20);
int sum(int a, int b = 20);
#elif // 编译通过
int sum(int a, int b = 20);
int sum(int a = 10, int b);
#endif
int main() {
int a = 10, b = 20;
int ret = sum(a, b);
cout << "ret: " << ret << endl;
ret = sum(a);
/*
a 使用默认值
压栈: 时压入 a 的值和 20.
push 14H
mov ecx, dowrd ptr[ebp - 4]
push ecx
call sum
*/
ret = sum();
// 压入 10 和 20.
}
int sum(int a, int b) {
return a + b;
}
掌握内联函数
内联函数和普通函数的区别?
- 内联函数:在编译过程中没有函数调用的开销,因为在函数的调用点函数被直接展开处理。
- 内联函数将不再产生相应的函数符号。
- 函数定义时加上
inline
并不一定会让函数变成内联函数,仅仅是对编译器的一种建议。如递归很可能不会被处理为内联函数。
#include <iostream>
using namespace std;
#define IS_INLINE 1
#if IS_INLINE
inline
#endif
int sum(int a, int b = 20) {
return a + b;
}
int main() {
int a = 10, b = 20;
int ret = sum(a, b);
// 此处有标准的函数调用过程
// 当函数调用的开销的占比过高,建议使用内联函数
cout << "ret: " << ret << endl;
}
注意:inline
在 debug 版本上是不起作用的。
验证:inline
在 release 版能出现
chipen@ubuntu:~/code/inlineTest$ cat main.cpp
#include <iostream>
using namespace std;
int sum(int a, int b) {
return a + b;
}
int main() {
int a = 10, b = 20;
int ret = sum(a, b);
return 0;
}
chipen@ubuntu:~/code/inlineTest$ g++ -c main.cpp -O2
chipen@ubuntu:~/code/inlineTest$ objdump -t main.o
main.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 main.cpp
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .text.startup 0000000000000000 .text.startup
0000000000000000 *UND* 0000000000000000 _ZSt21ios_base_library_initv
0000000000000000 g F .text 0000000000000008 _Z3sumii # sum 函数的符号
0000000000000000 g F .text.startup 0000000000000007 main
chipen@ubuntu:~/code/inlineTest$ vim main.cpp # 给 sum 函数加上 inline
chipen@ubuntu:~/code/inlineTest$ g++ -c main.cpp -O2
chipen@ubuntu:~/code/inlineTest$ objdump -t main.o
main.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 main.cpp
0000000000000000 l d .text.startup 0000000000000000 .text.startup
0000000000000000 *UND* 0000000000000000 _ZSt21ios_base_library_initv
0000000000000000 g F .text.startup 0000000000000007 main
# 可见,添加内联以后,sum 函数不再有对应的符号,而是被直接替换
chipen@ubuntu:~/code/inlineTest$ g++ -c main.cpp -O0
chipen@ubuntu:~/code/inlineTest$ objdump -t main.o
main.o: file format elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 main.cpp
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .text._Z3sumii 0000000000000000 .text._Z3sumii
0000000000000000 l O .rodata 0000000000000001 _ZNSt8__detail30__integer_to_chars_is_unsignedIjEE
0000000000000001 l O .rodata 0000000000000001 _ZNSt8__detail30__integer_to_chars_is_unsignedImEE
0000000000000002 l O .rodata 0000000000000001 _ZNSt8__detail30__integer_to_chars_is_unsignedIyEE
0000000000000000 *UND* 0000000000000000 _ZSt21ios_base_library_initv
0000000000000000 w F .text._Z3sumii 0000000000000018 _Z3sumii
0000000000000000 g F .text 0000000000000033 main
# 在只用 -O0 的低优化等级时,inline 需求依然被忽略,可见 inline 只是一种建议行为
函数重载
chipen@ubuntu:~/code/inlineTest$ cat test01.cpp
#include <iostream>
#include <cstring>
using namespace std;
bool compare(int a, int b) {
cout << "int, int" << endl;
return a > b;
}
bool compare(double a, double b) {
cout << "double, double" << endl;
return a > b;
}
bool compare(const char *a, const char *b) {
cout << "const char*, const char*" << endl;
return strcmp(a, b) > 0;
}
int main() {
compare(10, 20);
compare(10.0, 20.0);
compare("hello", "world");
return 0;
}
chipen@ubuntu:~/code/inlineTest$ ./test01
int, int
double, double
const char*, const char*
注意1: 一组重载函数指的是: 在同一作用域下的, 函数名相同作用域不同的函数
如当尝试在 main 函数中添加如下声明
chipen@ubuntu:~/code/inlineTest$ cat test01.cpp
#include <iostream>
#include <cstring>
using namespace std;
bool compare(int a, int b) {
cout << "int, int" << endl;
return a > b;
}
bool compare(double a, double b) {
cout << "double, double" << endl;
return a > b;
}
bool compare(const char *a, const char *b) {
cout << "const char*, const char*" << endl;
return strcmp(a, b) > 0;
}
int main() {
bool compare(int a, int b);
compare(10, 20);
compare(10.0, 20.0);
compare("hello", "world");
return 0;
}
chipen@ubuntu:~/code/inlineTest$ g++ test01.cpp -o test01
test01.cpp: In function ‘int main()’:
test01.cpp:26:17: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
26 | compare("hello", "world");
| ^~~~~~~
| |
| const char*
test01.cpp:23:26: note: initializing argument 1 of ‘bool compare(int, int)’
23 | bool compare(int a, int b);
| ~~~~^
test01.cpp:26:26: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
26 | compare("hello", "world");
| ^~~~~~~
| |
| const char*
test01.cpp:23:33: note: initializing argument 2 of ‘bool compare(int, int)’
23 | bool compare(int a, int b);
| ~~~~^
注意2:同一类型,加不加const
在编译器眼中没有区别。
void func(int a) {}
void func(const int a) {}
// 报错
void func(int *a) {}
void func(const int *a) {}
// 编译通过
void func(int *a) {}
void func(int const *a) {}
// 报错
为什么 C++ 支持函数重载,C 语言不支持函数重载?
在对符号表中函数命名时,C++采取了更能准确描述一个函数的命名方式,而 C 语言直接用函数名字作为符号名。
因此,C 和 C++ 中的函数由于函数名不同,不能直接调用,会在链接时发生无法解析的外部符号的错误,因为在符号表中找不到对应的函数符号名。
要实现 C 和 C++ 之间都可以调用的函数,应该这样定义
#ifdef __cplusplus
extern "C" {
#endif
int sum(int a, int b) {
return a + b;
}
#ifdef __cplusplus
}
#endif
掌握const
的用法
基本理解:const
修饰的常变量不能作为左值使用。
常变量 != 常量,例如:
chipen@ubuntu:~/code/inlineTest$ cat test02.cpp
#include <cstdio>
int main() {
const int a = 20;
int *p = (int *)&a;
*p = 30;
printf("%d %d %d\n", a, *p, *(&a));
return 0;
}
chipen@ubuntu:~/code/inlineTest$ ./test02
20 30 30
这是个不可思议的现象。原因是:在编译器的前端阶段,a 被认为是不可能被修改的,故在后文中被做了直接替换,但是 *p, *(&a)
要求必须访问内存,故得到了 20 30 30。
证明这是一种优化行为:
chipen@ubuntu:~/code/inlineTest$ cat test02.cpp
#include <cstdio>
int main() {
volatile const int a = 20; # 不让编译器优化 a
int *p = (int *)&a;
*p = 30;
printf("%d %d %d\n", a, *p, *(&a));
return 0;
}
chipen@ubuntu:~/code/inlineTest$ ./test02
30 30 30 # 符合预期
const
修饰的量常出现的错误是:
- 常量不能再作为左值 <= 直接修改常量的值
- 不能把常量的地址泄露给一个普通的指针或者普通的引用变量 <= 可能间接修改常量的值
const
和一级指针的结合:(const
修饰的是离它最近的类型)
const int * p
-> 指针指向的变量不能修改int const * p
-> 同上int *const p
-> 指针本身不能修改const int *const p
-> 指针本身和指针所指的变量都不能修改
int a = 10;
int *p1 = &a; // 通过, 无类型转换
const int *p2 = &a; // 隐式类型转换
int *const p3 = &a; // 隐式类型转换
const int b = 10;
int *p4 = &b; // 不通过, (const int *) -> (int *)
const int *p5 = &b; // 通过
int *const p6 = &b; // 不通过, (const int *) -> (int *const)
// 总结: 权限只能缩小不能扩大
const
和二级/多级指针的结合
int a = 20;
int *p = &a;
const int **p1 = &p;
// 典型错误,这样赋值会让原来的普通指针 p 指向被 const 修饰的变量
/*
正确写法:
int a = 20;
const int *p = &a;
cosnt int **p1 = &p;
*/
int *const *p2 = &p;
int **const p3 = &p;
左值引用和右值引用
int main() {
int a = 10; // 左值,有地址有名字,值可以修改
int &b = a;
int &&c = 20; // 20 为右值,没内存,没名字
c = 30; // 右值引用可以修改
const int &b = 20;
/*
int temp = 20;
temp -> b
*/
const int &d = 20;
return 0;
}
右值引用:
int &&c = 20;
专门用来引用右值类型,指令上可以自动产生临时量,然后直接引用临时量。右值引用变量本身是一个左值,只能用左值引用来引用它。
不能用一个右值引用变量引用一个左值。
const
、指针和引用的结合应用
int main() {
// 写一句代码,在内存的 0x0018ff44 处写一个 4 字节的 10
int *p = (int *)0x0018ff44;
int *&&p = (int *)0x0018ff44;
int *const &p = (int *)0x0018ff44;
return 0;
}
深入理解 c++ 的 new 和 delete
new 和 delete 为C++ 的运算符
malloc 和 free 为 C 的库函数
开辟内存失败时:
malloc 需要返回值与 nullptr 比较,为空时失败。
new 会抛异常。
new 有多少种?
int *p1 = new int(20);
int *p2 = new (nothrow) int;
int *p3 = new const int(40);
// 定位 new
int data = 0;
int *p4 = new (&data) int(50);
cout << "data: " << data << endl; // 输出 data: 0