1、参考引用
- C++高级编程(第4版,C++17标准)马克·葛瑞格尔
2、建议先看《21天学通C++》 这本书入门,笔记链接如下
- 21天学通C++读书笔记(文章链接汇总)
int x = 3;
int &xRef = x; // xRef 是 x 的另一个别名
xRef = 10; // 对 xRef 赋值会改变 x 的值
int &emptyRef; // 错误,必须初始化
int &unnamedRef1 = 5; // 无法编译
const int &unnamedRef2 = 5; // 正常编译
std::string getString() {
return "Hello world!";
}
std::string &string1 = getString(); // 无法编译
const std::string &string2 = getString(); // 正常编译
int x = 3, y = 4;
int &xRef = x;
xRef = y; // 将 y 的值 4 赋给 x,引用不会更新为指向 y
int* intP;
int* &ptrRef = intP; // ptrRef 是一个指向 intP 的引用,intP 是一个指向 int 值的指针
ptrRef = new int;
*ptrRef = 5;
int x = 3;
int &xRef = x;
int *xPtr = &xRef; // 取 x 引用(xRef)的地址,使 xRef 指向 x
*xPtr = 100; // x 的值也变为 100
注意:无法声明引用的引用或者指向引用的指针,例如,不允许使用 int&& 或 int&*
class MyClass {
public:
MyClass(int &ref) : mRef(ref) {}
private:
int &mRef;
};
默认的参数传递机制是按值传递:函数接收参数的副本,修改这些副本时,原始的参数保持不变
引用允许指定另一种向函数传递参数的语义:按引用传递。当使用引用参数时,函数将引用作为参数。如果引用被修改,最初的参数变量也会被修改。例如,下面给出了一个简单的交换函数,交换两个 int 变量的值
void swap(int &first, int &second) {
int temp = first;
first = second;
second = temp;
}
int x = 5, y = 6;
swap(x, y);
就像无法用常量初始化普通引用变量一样,不能将常量作为参数传递给 “按非 const 引用传递” 的函数
swap(3, 4); // 编译错误
将指针转换为引用
int x = 5, y = 6;
int *xp = &x, *yp = &y;
swap(*xp, *yp);
void handleMessage(std::string &&message) {
cout << "handleMessage with rvalue reference: " << message << endl;
}
handleMessage("Hello World");
std::string a = "Hello ";
std::string b = "World";
handleMessage(a + b);
要优先使用引用。也就是说,只有在无法使用引用的情况下,才使用指针
可使用 const 来 “保护” 变量不被修改。这个关键字的一个重要用法是替换 #define 来定义常量
const double PI = 3.1415926535;
还可使用 const 指定函数或方法的参数保持不变
void func(const int param) {
// ...
}
const 指针
int const *ip;
ip = new int[10];
ip[4] = 5; // 编译错误,无法修改 ip 所指的值
int* const ip = nullptr;
ip = new int[10]; // 编译错误,无法修改 ip 的指向
int const * const ip = nullptr;
const 引用
int z;
const int &zRef = z;
zRef = 4; // 编译错误,由于将 const 应用到 int,因此无法对 zRef 赋值
z = 5; // 但仍然可以修改 z 的值,直接改变 z 而不是通过引用
const 引用经常用作参数。如果为了提高效率,想按引用传递某个值,但不想修改这个值,可将其标记为 const 引用
void doSomething(const BigClass &arg) { // 通常默认为 const 引用
// ...
}
const int getArraySize() {
return 32;
}
int main() {
int myArray[getArraySize()]; // 无效
return 0;
}
constexpr int getArraySize() { // 常量表达式
return 32;
}
int main() {
int myArray[getArraySize()]; // ok
return 0;
}
C++ 的每个源文件都是单独编译的,编译得到的目标文件会彼此链接。C++ 源文件中的每个名称,包括函数和全局变量,都有一个内部或外部的链接
// FirstFile.cpp
void f(); // 提供了原型但没给出定义
int main() {
f(); // f() 函数默认具有外部链接,可从 AnotherFile.cpp 调用该函数
return 0;
}
// AnotherFile.cpp
#include
void f(); // 提供了原型和定义
// static void f(); // 此时 FirstFile.cpp 无法调用,因为 f() 为静态链接
void f() {
std::cout << "f\n";
}
将 static 用于内部链接的另一种方式是使用匿名名称空间,可将变量或函数封装到一个没有名字的名称空间,而不是使用 static,如下所示
#include
namespace {
void f();
void f() {
std::cout << "f\n";
}
}
extern 关键字
void performTask() {
static bool initialized = false;
if (!initialized) {
cout << "initialized" << endl;
initialized = true;
}
// 执行期望的任务
}
using IntPtr = int*;
using StringVector = std::vector<std::string>; // 类型别名可包括作用域限定符
using MatchFunction = bool(*)(int, int);
void findMatches(int values1[], int values2[], size_t numValues, MatchFunction matcher) {
for (size_t i = 0; i < numValues; i++) {
if (matcher(values1[i], values2[i])) {
cout << "Match found at position " << i <<
" (" << values1[i] << ", " << values2[i] << ")" << endl;
}
}
}
bool intEqual(int num1, int num2) {
return num1 == num2;
}
int arr1[] = {2, 5, 6, 9, 10, 1, 1};
int arr2[] = {4, 4, 2, 9, 0, 3, 4};
size_t arrSize = std::size(arr1); // C++17 前用法:sizeof(arr1) / sizeof(arr1[0]);
cout << "Calling findMatches() using intEqual():" << endl;
findMatches(arr1, arr2, arrSize, &intEqual);
// 结果输出
Calling findMatches() using intEqual():
Match found at position 3 (9, 9)
using IntPtr = int*;
typedef int* IntPtr; // 可读性较差
using FunctionType = int(*)(char, double);
typedef int(*FunctionType)(char, double); // 非常复杂
extern void ThirdPartyLibraryMethod(char* str);
void f(const char* str) {
ThirdPartyLibraryMethod(const_cast<char*>(str));
}
int i = 3;
int j = 4;
// 只需要把两个操作数之一设置为 double,就可确保执行浮点数除法
double result = static_cast<double>(i) / j;
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
public:
virtual ~Derived() = default;
};
int main() {
Base* b;
Derived* d = new Derived();
b = d;
d = static_cast<Derived*>(b); // 需要向下转换
Base base;
Derived derived;
Base& br = derived;
Derived& dr = static_cast<Derived&>(br);
return 0;
}
- static_cast() 无法将某种类型的指针转换为不相关的其他类型的指针
- 如果没有可用的转换构造函数,static_cast() 无法将某种类型的对象直接转换为另一种类型的对象
- static_cast() 无法将 const 类型转换为非 const 类型,无法将指针转换为 int
class X {};
class Y {};
int main() {
X x;
Y y;
X* xp = &x;
Y* yp = &y;
xp = reinterpret_cast<X*>(yp);
void* p = xp;
xp = reinterpret_cast<X*>(P);
X& xr = x;
Y& yr = reinterpret_cast<Y&>(x);
return 0;
}
使用 reinterpret_cast() 时要特别小心,因为在执行转换时不会执行任何类型检测
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
public:
virtual ~Derived() = default;
};
Base* b;
Derived* d = new Derived();
b = d;
d = dynamic_cast<Derived*>(b);
运行时类型信息存储在对象的虚表中。因此,为使用 dynamic_cast(),类至少要有一个虚方法。如果类不具有虚表,尝试使用 dynamic_cast() 将导致编译错误
#include
#include
class Demo {
public:
static int get() {
return 5;
}
};
int get() {
return 10;
}
namespace NS {
int get() {
return 20;
}
}
int main() {
auto pd = std::make_unique<Demo>();
Demo d;
std::cout << pd->get() << " ";
std::cout << d.get() << " ";
std::cout << NS::get() << " ";
std::cout << Demo::get() << " ";
std::cout << ::get() << " ";
std::cout << get() << " ";
}
// 输出结果
5 5 20 5 10 10
C++ 有许多可在代码中使用的标准字面量
C++11 允许定义自己的字面量
C++ 定义了如下标准的用户定义字面量
注意,这些标准的用户定义字面量并非以下画线开头
auto myString = "Hello World"s;
// 需要 using namespace std::string_literals;
auto myStringView = "Hello World"sv;
// 需要 using namespace std::string_view_literals;
auto myDuration = 42min;
// 需要 using namespace std::chrono_literals;
auto myComplexNumber = 1.3i;
// 需要 using namespace std;
#ifndef LOGGER_H
#define LOGGER_H
class Logger {
// ...
};
#endif
#pragma once
class Logger {
// ...
};
#pragma once
#include
class Preferences; // 前置声明
class Logger {
public:
static void setPreferences(const Preferences& prefs);
static void logError(std::string_view error);
};