1、参考引用
- C++高级编程(第4版,C++17标准)马克·葛瑞格尔
2、建议先看《21天学通C++》 这本书入门,笔记链接如下
- 21天学通C++读书笔记(文章链接汇总)
// helloworld.cpp
/*
helloworld.cpp
*/
#include
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
注释
预处理指令
#ifndef MYHEADER_H
#define MYHEADER_H
//...the contents of this header filef
#endif
// 或(上下等价)
#pragma once
//...the contents of this header filef
main() 函数
输入输出流
// std::cout 用于输出信息
// std::cerr 用于输出错误信息
// std::endl 代表序列末尾,换行,表明一行末尾的另一种方法是使用 \n 字符(转义字符)
// std::cin 接受键盘输入信息
// 注:printf() 和 scanf() 未提供类型安全,不建议使用
// 头文件 namespaces.h
namespace mycode {
void foo();
}
// namespaces.cpp
// 名称空间中还可实现方法或函数
#include
#include "namespaces.h"
void mycode::foo() {
std::cout << "foo() called in the mycode namespace" << std::endl;
}
// usingnamespaces.cpp
#include "namespaces.h"
using namespace mycode;
int main() {
mycode::foo(); // 调用 mycode 名称空间中的 foo() 函数
foo(); // 使用 using 后也可隐式调用
return 0;
}
切勿在头文件中使用 using 指令或 using 声明,否则添加你的头文件的每个人都必须使用它
namespace MyLibraries::Networking::FTP {}
namespace MyFTP = MyLibraries::Networking::FTP;
float myFloat = 3.14f;
int i1 = (int)myFloat; // 方法一,目前最常使用,但不建议
int i2 = int(myFloat); // 方法二,很少使用
int i3 = static_cast<int>(myFloat); // 方法三,最建议使用
枚举类型
enum PieceType {
PieceTypeKing,
PieceTypeQueen,
PieceTypeRook,
PieceTypePawn
};
还可为枚举成员指定整型值
enum PieceType {
PieceTypeKing = 1,
PieceTypeQueen,
PieceTypeRook = 10,
PieceTypePawn
};
强类型枚举
enum class PieceType {
King = 1,
Queen,
Rook = 10,
Pawn
}
PieceType piece = PieceType::King;
enum class PieceType : unsigned long {
King = 1,
Queen,
Rook = 10,
Pawn
}
建议用类型安全的 enum class 枚举替代类型不安全的 enum 枚举
结构
// employeestruct.h
#pragma once
struct Employee {
char firstInitial;
char lastInitial;
int employeeNumber;
int salary;
};
// structtest.cpp
#include
#include "employeestruct.h"
using namespace std;
int main() {
// Create and populate an employee.
Employee anEmployee;
anEmployee.firstInitial = 'M';
anEmployee.lastInitial = 'G';
anEmployee.employeeNumber = 42;
anEmployee.salary = 80000;
// Output the values of an employee.
cout << "Employee: " << anEmployee.firstInitial << anEmployee.lastInitial << endl;
cout << "Number: " << anEmployee.employeeNumber << endl;
cout << "Salary: $" << anEmployee.salary << endl;
return 0;
}
if/else 语句
if (i > 4) {
// Do something.
} else if {
// Do something else.
} else {
// Do something else.
}
C++17 允许在 if 语中包括一个初始化器
if (<initializer> ; <conditional expression>) ( <body> }
// 示例:初始化器获得一名雇员,以及检查所检索雇员的薪水是否超出 1000 的条件
// 只有满足条件才执行 if 语句体
if (Employee employee = GetEmployee(); employee.salary > 1000) {
...
}
switch 语句
switch(menuItem) {
case OpenMenuItem:
// Code to open a file
break;
case SaveMenuItem:
// Code to save a file
break;
default:
// Code to give an error message
break;
}
switch (<initializer>; <expression>) {<body>}
条件运算符
// i 大于 2 吗?如果是真的,结果就是 yes,否则结果就是 no
std::cout << ((i > 2) ? "yes" : "no");
// 函数声明
void myFunction(int i, char c);
// 函数定义
void myFunction(int i, char c) {
std::cout << "the value of i is" << i << std::endl;
std::cout << "the value of c is" << c << std::endl;
}
// 函数调用
myFunction(8,a');
auto addNumbers(int number1, int number2) {
return number1 + number2;
}
int addNumbers(int number1, int number2) {
std::cout << "Entering function " << _func_ << std::endl;
return number1 + number2;
}
int myArray[3];
myArray[0];
myArray[1];
myArray[2];
int myArray[3] = {0};
int myArray[3] = {};
int myArray[] = {1, 2, 3, 4};
char ticTacToeBoard[3][3];
ticTacToeBoard[1][1] = 'o';
array<int, 3> arr = {9, 8, 7};
cout << "Array size = " << arr.size() << endl;
cout << "2nd element = " << arr[1] << endl;
#include // 包含头文件
vector<int> myVector = {11, 22};
myVector.push_back(33); // 向 vector 中动态添加元素
myVector.push_back(44);
cout << "1st element: " << myVector[0] << endl;
std::array<int, 3> values = {11,22,33};
auto [x, y, z] = values;
while 循环
int i = 0;
while (i < 5) {
std::cout << "This is silly." << std::endl;
++i;
}
do/while 循环
int i = 100;
do {
std::cout << "This is silly." << std::endl;
++i;
} while (i < 5);
for 循环
for (int i = 0; i < 5; ++i) {
std::cout << "This is silly." << std::endl;
}
基于区间的 for 循环
std::array<int, 4> arr = {1, 2, 3, 4};
for (int i : arr) {
std::cout << i << std::endl;
}
#include
#include
using namespace std;
int makeSum(initializer_list<int> lst) {
int total = 0;
for (int value : lst) {
total += value;
}
return total;
}
int main() {
int a = makeSum({1, 2, 3});
int b = makeSum({10, 20, 30, 40, 50, 60});
cout << a << endl;
cout << b << endl;
return 0;
}
#include
string myString = "Hello, World";
cout << "The value of myString is " << myString << endl;
cout << "The second letter is " << myString[1] << endl;
C++ 程序中的内存分为两个部分:堆栈和堆
堆栈(和栈是一个意思)
- 堆栈帧为每个函数提供了独立的内存空间。如果在 foo() 堆栈中声明了一个变量,那么除非专门要求,否则调用 bar() 函数不会更改该变量
- foo() 函数执行完毕时,堆栈就会消失,该函数中声明的所有变量都不会再占用内存
- 在堆栈上分配内存的变量不需要程序员手动释放内存(删除),这个过程是自动完成的
堆栈帧
堆
// 应避免使用未初始化的变量/指针
int *myIntegerPointer = new int; // 使用 new 操作符分配内存
int *myIntegerPointer = nullptr; // 如果不希望立即分配内存,可以把它们初始化为空指针
*myIntegerPointer = 8; // 没改变指针,只是改变指针指向的内存
delete myIntegerPointer;
myIntegerPointer = nullptr;
int i = 8;
int *myIntegerPointer = &i;
Employee *anEmployee = getEmployee();
cout << (*anEmployee).salary << endl; // 同下等价
cout << anEmployee->salary << endl; // -> 运算符允许同时对指针解除引用并访问字段
int arraySize = 8;
int *myVariableSizedArray = new int[arraySize];
myVariableSizedArray[3] = 2;
// 使用完这个数组后,应该将其从堆中删除,这样其他变量就可以使用这块内存
delete[] myVariableSizedArray;
myVariableSizedArray = nullptr;
避免使用 C 中的 malloc() 和 free(),而使用 new 和 delete,或者使用 new[] 和 delete[]
void func(char* str) {cout << "char* version" << endl;}
void func(int i) {cout <<"int version" << endl;}
int main() {
func(NULL); // NULL 不是指针,而等价于 0,所以调用整数版本
func(nullptr); // 真正的空指针常量 nullptr,调用 char* 版本
return 0;
}
智能指针对象在超出作用域时,例如在函数执行完毕后,会自动释放内存。C++ 中有两种最重要的智能指针
std::unique_ptr
/*
unique_ptr 是一个通用的智能指针,它可以指向任意类型的内存
所以它是一个模板,而模板需要用尖括号指定模板参数
在尖括号中必须指定 unique_ptr 要指向的内存类型
make_unique 为 C++14 引入
*/
auto anEmployee = make_unique<Employee>(); // 不再需要调用 delete,因为会自动完成
unique_ptr<Employee> anEmployee(new Employee); // C++11 标准
auto employees = make_unique<Employee[]>(10);
cout << "Salary: " << employees[0].salary << endl;
std::shared_ptr
auto anEmployee = make_shared<Employee>();
if (anEmployee) {
cout << "Salary: " << anEmployee->salary << endl;
}
shared_ptr<Employee[]> employees(new Employee[10]);
cout << "Salary: " << employees[0].salary << endl;
const int versionNumberMajor = 2;
const int versionNumberMinor = 1;
const std::string productName = "Super Hyper Net Modulator";
void mysteryFunction(const std::string *someString) {
*someString ="Test"; // 不允许修改
}
int main() {
std::string myString = "The string";
mysteryFunction(&myString);
return 0;
}
int x = 42;
int &xReference = x;
// 不会影响传递给它的变量,因为变量是按照值传递的
// 因此函数接收的是传递给它的值的一个副本
void addOne(int i) {
i++;
}
// 使用了引用,因此可以改变原始变量的值
void addOne(int &i) {
i++;
}
void printString(const std::string &myString) {
std::cout << myString << std::endl;
}
int main() {
std::string someString = "Hello world!";
printString(someString);
printString("Hello World!");
return 0;
}
#include
double divideNumbers(double numerator, double denominator) {
if (denominator == 0) {
throw invalid_argument("Denominator cannot be 0."); // 函数立刻结束而不会返回值
}
return numerator / denominator;
}
// 捕获异常并处理
try {
std::cout << divideNumbers(2.5,0.5) << std::endl; // 返回 5
std::cout << divideNumbers(2.3,0) << std::endl; // 抛出异常,不返回值,并直接跳到 catch 块
std::cout << divideNumbers(4.5,2.5) << std::endl; // 程序已跳转,该行不执行
} catch (const invalid_argument &exception) {
std::cout << "Exception caught:" << exception.what() << std::endl;
}
#include
const std::string message = "Test";
const std::string &foo() {
return message;
}
// 因为 auto 去除了引用和 const 限定符,且 f1 是 string 类型,所以建立一个副本
auto f1 = foo();
// 如果不需要副本,可使用 auto& 或 const auto&
const auto &f2 = foo();
// 编译器推断出 y 的类型是 int,因为这是 x 的类型
int x = 123;
decltype(x) y = 456;
decltype(foo()) f2 = foo();
// AirlineTicket.h
#pragma once
#include
class AirlineTicket {
public:
AirlineTicket(); // 当创建类的对象时会自动调用构造函数
~AirlineTicket(); // 当销毁对象时会自动调用析构函数
double calculatePriceInDollars() const;
const std::string &getPassengerName() const;
void setPassengerName(const std::string& name);
int getNumberOfMiles() const;
void setNumberOfMiles(int miles);
bool hasEliteSuperRewardsStatus() const;
void setHasEliteSuperRewardsStatus(bool status);
private:
std::string mPassengerName;
int mNumberOfMiles;
bool mHasEliteSuperRewardsStatus;
};
AirlineTicket::AirlineTicket() : mPassengerName("Unknown Passenger"),
mNumberOfMiles(0),
mHasEliteSuperRewardsStatus(false) {}
AirlineTicket::AirlineTicket() {
mPassengerName = "Unknown Passenger";
mNumberOfMiles = 0;
mHasEliteSuperRewardsStatus = false;
}
private:
std::string mPassengerName = "Unknown Passenger";
int mNumberOfMiles = 0;
bool mHasEliteSuperRewardsStatus = false;
// AirlineTicket.cpp
#include "AirlineTicket.h"
using namespace std;
AirlineTicket::AirlineTicket() : mPassengerName("Unknown Passenger"),
mNumberOfMiles(0),
mHasEliteSuperRewardsStatus(false) {}
AirlineTicket::~AirlineTicket() {}
double AirlineTicket::calculatePriceInDollars() const {
if (hasEliteSuperRewardsStatus()) {
// Elite Super Rewards customers fly for free!
return 0;
}
return getNumberOfMiles() * 0.1;
}
const string &AirlineTicket::getPassengerName() const {
return mPassengerName;
}
...
// AirlineTicketTest.cpp
#include
#include
#include "AirlineTicket.h"
using namespace std;
int main() {
// 1. 基于堆栈的类的使用方法
AirlineTicket myTicket;
myTicket.setPassengerName("Sherman T. Socketwrench");
myTicket.setNumberOfMiles(700);
double cost = myTicket.calculatePriceInDollars();
cout << "This ticket will cost $" << cost << endl;
// 2. 基于堆的类的使用方法(使用智能指针)
auto myTicket2 = make_unique<AirlineTicket>();
myTicket2->setPassengerName("Laudimore M. Hallidue");
myTicket2->setNumberOfMiles(2000);
myTicket2->setHasEliteSuperRewardsStatus(true);
double cost2 = myTicket2->calculatePriceInDollars();
cout << "This other ticket will cost $" << cost2 << endl;
// No need to delete myTicket2, happens automatically
// 3. 基于堆的类的使用方法(不使用智能指针)(不推荐使用该方式)
AirlineTicket *myTicket3 = new AirlineTicket();
// ... Use ticket 3
delete myTicket3; // delete the heap object!
return 0;
}
struct CircleStruct {
int x, y;
double radius;
};
class CircleClass {
public:
CircleClass(int x, int y, double radius)
: mX(x), mY(y), mRadius(radius) {}
private:
int mX, mY;
double mRadius;
};
CircleStruct myCirclel = {10,10,2.5};
CircleClass myCircle2(10,10,2.5);
// 其中 = 号是可选的
CircleStruct myCirclel = {10,10,2.5};
CircleClass myCircle2 = {10,10,2.5};
int *pArray = new int[4]{0,1,2,3};
class MyClass {
public:
MyClass() : mArray{0,1,2,3} {}
private:
int mArray[4];
};
管理公司雇员记录的程序应该灵活并具有有效的功能,这个程序包含的功能有
程序的代码分为三个部分
#pragma once // 防止文件被包含多次
#include
// 自定义 Records 名称空间
namespace Records {
const int kDefaultStartingSalary = 30000; // 设置新雇员默认起薪
class Employee {
public:
Employee() = default; // 显式的默认构造函数
// 包含接收姓名的构造函数
Employee(const std::string& firstName, const std::string& lastName);
void promote(int raiseAmount = 1000); // 设定了默认值
void demote(int demeritAmount = 1000); // 设定了默认值
void hire(); // Hires or rehires the employee
void fire(); // Dismisses the employee
void display() const;// Outputs employee info to console
// 提供修改 set 或查询 get 雇员信息的机制
void setFirstName(const std::string& firstName);
const std::string& getFirstName() const;
void setLastName(const std::string& lastName);
const std::string& getLastName() const;
void setEmployeeNumber(int employeeNumber);
int getEmployeeNumber() const;
void setSalary(int newSalary);
int getSalary() const;
bool isHired() const;
private:
std::string mFirstName;
std::string mLastName;
int mEmployeeNumber = -1;
int mSalary = kDefaultStartingSalary;
bool mHired = false;
};
}
#include
#include "Employee.h"
using namespace std;
namespace Records {
Employee::Employee(const std::string &firstName, const std::string &lastName)
: mFirstName(firstName), mLastName(lastName) {}
// 只是用一些新值调用 setSalary() 方法
// 注意:整型参数的默认值不显示在源文件中,只能出现在函数声明中,不能出现在函数定义中
void Employee::promote(int raiseAmount) {
setSalary(getSalary() + raiseAmount);
}
void Employee::demote(int demeritAmount) {
setSalary(getSalary() - demeritAmount);
}
// 正确设置了 mHired 成员
void Employee::hire() {
mHired = true;
}
void Employee::fire() {
mHired = false;
}
// 使用控制台输出流显示当前雇员的信息
void Employee::display() const{
cout << "Employee: " << getLastName() << ", " << getFirstName() << endl;
cout << "-------------------------" << endl;
cout << (isHired() ? "Current Employee" : "Former Employee") << endl;
cout << "Employee Number: " << getEmployeeNumber() << endl;
cout << "Salary: $" << getSalary() << endl;
cout << endl;
}
// 许多获取器(get)和设置器(set)执行获取值以及设置值的任务
// 使用这些获取器和设置器的方式要优于将数据成员设置为 public
// 1. 方便设置断点,简化调试
// 2. 修改类中存储数据的方式时,只需修改这些获取器和设置器
void Employee::setFirstName(const string &firstName) {
mFirstName = firstName;
}
const string& Employee::getFirstName() const {
return mFirstName;
}
void Employee::setLastName(const string& lastName) {
mLastName = lastName;
}
const string& Employee::getLastName() const {
return mLastName;
}
void Employee::setEmployeeNumber(int employeeNumber) {
mEmployeeNumber = employeeNumber;
}
int Employee::getEmployeeNumber() const {
return mEmployeeNumber;
}
void Employee::setSalary(int salary) {
mSalary = salary;
}
int Employee::getSalary() const {
return mSalary;
}
bool Employee::isHired() const {
return mHired;
}
}
#pragma
#include
#include
#include "Employee.h"
namespace Records {
const int kFirstEmployeeNumber = 1000;
class Database {
public:
Employee &addEmployee(const std::string &firstName,
const std::string &lastName);
// 1. 允许按雇员号进行检索
Employee &getEmployee(int employeeNumber);
// 2. 要求提供雇员姓名
Employee &getEmployee(const std::string &firstName,
const std::string &lastName);
// 输出所有雇员、当前在职雇员和离职雇员的方法
void displayAll() const;
void displayCurrent() const;
void displayFormer() const;
private:
std::vector<Employee> mEmployees;
// 跟踪新雇员的雇员号
int mNextEmployeeNumber = kFirstEmployeeNumber;
};
}
#include
#include
#include "Database.h"
using namespace std;
namespace Records {
Employee &Database::addEmployee(const string &firstName,
const string &lastName) {
// 使用输入参数初始化成员变量
Employee theEmployee(firstName, lastName);
// 数据成员 mNextEmployeeNumber 值递增,因此下一个雇员将获得新编号
theEmployee.setEmployeeNumber(mNextEmployeeNumber++);
theEmployee.hire(); // 将其聘用状态设置为 "已聘用"
mEmployees.push_back(theEmployee);
// 返回 mEmployees 向量中的最后一个元素,即新添加的员工
return mEmployees[mEmployees.size() - 1];
}
Employee &Database::getEmployee(int employeeNumber) {
// 基于区间的 for 循环遍历 mEmployees 中所有雇员
for (auto &employee : mEmployees) {
if (employee.getEmployeeNumber() == employeeNumber) {
return employee;
}
}
throw logic_error("No employee found.");
}
Employee &Database::getEmployee(const string &firstName,
const string &lastName) {
for (auto &employee : mEmployees) {
if (employee.getFirstName() == firstName &&
employee.getLastName() == lastName) {
return employee;
}
}
throw logic_error("No employee found.");
}
void Database::displayAll() const {
for (const auto &employee : mEmployees) {
employee.display();
}
}
void Database::displayCurrent() const {
for (const auto &employee : mEmployees) {
if (employee.isHired()) {
employee.display();
}
}
}
void Database::displayFormer() const {
for (const auto &employee : mEmployees) {
if (!employee.isHired()) {
employee.display();
}
}
}
}
#include
#include
#include
#include "Database.h"
using namespace std;
using namespace Records;
int displayMenu();
void doHire(Database& db);
void doFire(Database& db);
void doPromote(Database& db);
int main(int argc, char *argv[]) {
Database employeeDB;
bool done = false;
while (!done) {
int selection = displayMenu();
switch (selection) {
case 0:
done = true;
break;
case 1:
doHire(employeeDB);
break;
case 2:
doFire(employeeDB);
break;
case 3:
doPromote(employeeDB);
break;
case 4:
employeeDB.displayAll();
break;
case 5:
employeeDB.displayCurrent();
break;
case 6:
employeeDB.displayFormer();
break;
default:
cerr << "Unknown command." << endl;
break;
}
}
return 0;
}
int displayMenu() {
int selection;
cout << endl;
cout << "Employee Database" << endl;
cout << "-----------------" << endl;
cout << "1) Hire a new employee" << endl;
cout << "2) Fire an employee" << endl;
cout << "3) Promote an employee" << endl;
cout << "4) List all employees" << endl;
cout << "5) List all current employees" << endl;
cout << "6) List all former employees" << endl;
cout << "0) Quit" << endl;
cout << endl;
cout << "---> ";
cin >> selection;
return selection;
}
// 获取用户输入的新的雇员的姓名,并通知数据库添加这个雇员
void doHire(Database &db) {
string firstName;
string lastName;
cout << "First name?";
cin >> firstName;
cout << "Last name?";
cin >> lastName;
db.addEmployee(firstName, lastName);
}
// 要求数据库根据雇员号找到雇员的记录
void doFire(Database &db) {
int employeeNumber;
cout << "Employee number? ";
cin >> employeeNumber;
try {
Employee &emp = db.getEmployee(employeeNumber);
emp.fire();
cout << "Employee " << employeeNumber << " terminated." << endl;
} catch (const std::logic_error &exception) {
cerr << "Unable to terminate employee: " << exception.what() << endl;
}
}
void doPromote(Database& db) {
int employeeNumber;
int raiseAmount;
cout << "Employee number? ";
cin >> employeeNumber;
cout << "How much of a raise? ";
cin >> raiseAmount;
try {
Employee& emp = db.getEmployee(employeeNumber);
emp.promote(raiseAmount);
} catch (const std::logic_error& exception) {
cerr << "Unable to promote employee: " << exception.what() << endl;
}
}