探索 C++ (一)

参考书籍:
《Professional C++》、《C++ in Action》、《C++ 代码设计与重用》
《C++ 标准库》、《深度探索 C++ 对象模型》
《Modern C++ Programming with Test-Driven Development》、《Effective Modern C++》

程序之母

面向过程

1
2
3
4
5
6
#include <iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
return 0;
}

面向对象

1
2
3
4
5
6
7
8
9
#include <iostream>
class HelloWorld
{
public:
HelloWorld() { std::cout << "Hello "; }
~HelloWorld() { std:cout << "World!\n"; }
}
HelloWorld helloWorld;
int main() { }

基础

预处理指令

  • include

  • define

  • ifdef [key] … #endif 或 #ifndef [key] … #endif

  • pragma [xyz] : e.g. -> 避免文件被多次包含(#paragma once,文件第一行)

main() 函数

基本 I/O 流:std::cout、std:cerr

命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// namespaces.h
namespace mycode {
void foo();
}
// namespaces.cpp
#include <iostream>
#include "namespaces.h"
using namespace std;
namespace mycode {
void foo() {
cout << "foo() called in the mycode namespace" << endl;
}
}
// test.cpp
#include "namespaces.h"
using namespace mycode;
int main()
{
foo();
return 0;
}

变量

  • 未初始化经常是 bug 之源;
  • int i3 = static_cast(myFloat);

字面量

  • C++14 -> 0b1111011
  • C++14 -> int number1 = 23’456’78; float number2 = 0.123’456’7f;

操作符:必要时请加括号形成良好清晰的编程风格!

类型推断:关键字 -> auto、decltype

  • C++11 : auto x = 123; 或 auto result = getFoo();
  • C++14 :int x = 123; decltype(x) y = 456;

两种非基本类型

  • 枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
// 普通枚举类型
enum PieceType { PieceTypeKing, PieceTypeQueen, PieceTypeRook, PieceTypePawn };
// ...
PieceType myPiece= 0;
// ...
enum PieceType { PieceTypeKing = 1, PieceTypeQueen, PieceTypeRook = 10, PieceTypePawn };
//
// 强类型枚举
enum class MyEnum {
EnumValue1, EnumValue2 = 10, EnumValue3
};
MyEnum value1 = MyEnum::EnumValue1;
if (MyEnum:EnumValue3 == 11) { // } // 非法比较
  • 结构体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// employeestruct.h
struct Employee {
char firstInitial;
char lastInitial;
int employeeNumber;
int salary;
};
// employeestruct.cpp
#include <iostream>
#include "employeestruct.h"
using namespace std;
int main()
{
Employee anEmployee; // Create and populate an employee.
anEmployee.firstInitial = 'M';
anEmployee.lastInitial = 'G';
anEmployee.employeeNumber = 42;
anEmployee.salary = 80000;
cout << "Employee: " << anEmployee.firstInitial << anEmployee.lastInitial << endl;
cout << "Number: " << anEmployee.employeeNumber << endl;
cout << "Salary: $" << anEmployee.salary << endl;
return 0;
}

条件操作

  • if/else 语句
  • switch 语句
  • 条件操作符:cout << (( i > 2) ? “yes” : “no”);

循环

  • while 语句
  • do/while 语句
  • for 语句
  • 基于范围的 for 语句(the Range-based for loop)

数组

1
2
3
int myArray[3] = {0}; // 所有3个值都被初始化为0
int myArray[3] = {2}; // myArray[0] = 2,其余为0
int arr[] = {1, 2, 3, 4};
  • 标准库初探 :std::array(固定大小,总是知道自己的size,避免了类型转换带来的 bug,拥有便捷实用的迭代器; std::vector -> 动态大小)
1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <array>
using namespace std;
int main()
{
array<int, 3> arr = {9, 8, 7};
cout << "Array size = " << arr.size() << endl;
cout << "Element 2 = " << arr[1] << endl;
return 0;
}
  • 标准库初探
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <string>
#include <vector>
#include <iostream>
#include <iterator>
using namespace std;
int main()
{
vector<string> myVector = {"A first string", "A second string"};
// Add some strings to the vector using push_back
myVector.push_back("A third string");
myVector.push_back("The last string in the vector");
for (const auto& str : myVector) {
cout << str << endl;
}
for (auto iterator = cbegin(myVector); iterator != cend(myVector); ++iterator) { // 使用迭代器
cout << *iterator << endl;
}
return 0;
}

函数之更简洁的函数语法

  • C++11 :auto func(int i) -> int { return i + 2; }
  • C++14 :auto divideNumbers(double numerator, double denominator) { if (denominator == 0) { // } return numerator / denominator; }

指针相关

  • 堆栈与堆:在不需要时,确保释放在堆上分配的内存
  • int* myIntegerPointer = nullptr;
  • myIntegerPointer = new int; *myIntegerPointer = 8; delete myIntegerPointer; myIntegerPointer = nullptr;
  • Employee* anEmployee = getEmployee(); cout << anEmployee->salary << endl;
  • 一个动态分配数组内存的示例
1
2
3
int arraySize = 8;
int* myVariableSizedArray = new int[arraySize];
delete[] myVariableSizedArray;

动态分配数组内存示例

智能指针初探

  • std::unique_ptr :当离开作用域后,其自动释放内存;
  • C++11 -> std::unique_ptr anEmployee(new Employee);
  • C++14 -> auto anEmployee = std::make_unique();
  • std::shared_ptr :使用引用计数;
  • std::weak_ptr ;

引用:void addOne(int& i) { i++; }

异常初探

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <exception>
#include <stdexcept>
double divideNumbers(double numerator, double denominator)
{
if (denominator == 0) {
throw std::invalid_argument("Denominator cannot be 0.");
}
return numerator / denominator;
}
int main()
{
try {
std::cout << divideNumbers(2.5, 0.5) << std::endl;
std::cout << divideNumbers(2.3, 0) << std::endl;
std::cout << divideNumbers(4.5, 2.5) << std::endl;
} catch (const std::exception& exception) {
std::cout << "Exception caught: " << exception.what() << std::endl;
}
return 0;
}

const 使用

1
2
3
4
5
6
void mysteryFunction(const std::string* someString) { // 保护值
*someString = "Test"; // 错误,不会被编译.
}
void printString(const std::string& myString) { // 保护引用
std::cout << myString << std::endl;
}

类使用初探

1
2
3
4
5
6
AirlineTicket myTicket; // Stack-based
myTicket.setPassengerName("Sherman T. Socketwrench");
auto myTicket2 = make_unique<AirlineTicket>(); // Heap-based
myTicket2->setPassengerName("Laudimore M. Hallidue");
AirlineTicket* myTicket3 = new AirlineTicket(); // Heap-based
delete myTicket3; // delete the heap object!

使用 String 类

C 式字符串 ->
char* result = new char[strlen(str) + 1];

1
2
3
4
5
6
7
8
9
10
11
12
13
char text1[] = "abcdef";
size_t s1 = sizeof(text1); // is 7
size_t s2 = strlen(text1); // is 6
const char* text2 = "abcdef";
size_t s3 = sizeof(text2); // is platform-dependent
size_t s4 = strlen(text2); // is 6
// C 式字符串字面量
char* ptr = "hello"; // Assign the string literal to a variable.
ptr[1] = 'a'; // Undefined behavior!
const char* ptr = "hello";
ptr[1] = 'a'; // 错误!试图在只读内存中进行写操作
char arr[] = "hello"; // 编译器会自动创建相应大小的字符数组
arr[1] = 'a'; // 字符数组是可读写的
  • 使用
1
2
3
4
5
6
7
8
string A("12");
string B("34");
string C;
C = A + B; // C = "1234"
A += B; // A = "1234"
// C++14
auto string1 = "Hello World"; // string1 will be a const char*
auto string2 = "Hello World"s; // string2 will be an std::string

数字转换
string to_string(int val); string to_string(unsigned val);
string to_string(long val); string to_string(unsigned long val);
string to_string(long long val); string to_string(unsigned long long val);
string to_string(float val); string to_string(double val);

string to_string(long double val);

int stoi(const string& str, size_t idx=0, int base=10);
long stol(const string& str, size_t
idx=0, int base=10);
unsigned long stoul(const string& str, size_t idx=0, int base=10);
long long stoll(const string& str, size_t
idx=0, int base=10);
unsigned long long stoull(const string& str, size_t idx=0, int base=10);
float stof(const string& str, size_t
idx=0);
double stod(const string& str, size_t idx=0);
long double stold(const string& str, size_t
idx=0);

1
2
const string s = "1234";
int i = stoi(s); // i = 1234
  • Raw String Literals
1
2
3
4
string str = R"(Hello "World"!)";
string str = R"(Line 1
Line 2 with \t)";
string str = R"-(The characters )" are embedded in this string)-";"

注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
* saveRecord()
*
* Saves the given record to the database.
*
* Parameters:
* Record& rec: the record to save to the database.
* Returns: int
* An integer representing the ID of the saved record.
* Throws:
* DatabaseNotOpenedException if the openDatabase() method was not
* called first.
* Notes:
* Additional notes...
*/
//
/*
* Date | Change
*----------+--------------------------------------------------
* 110413 | REQ #005: <marcg> Do not normalize values.
* 110417 | REQ #006: <marcg> use nullptr instead of NULL.
*/

TDD 环境准备

  • 安装 gcc、cmake
  • 下载 CppUTest,并配置:export CPPUTEST_HOME=/home/user/cpputest
  • 安装 Google Mock :export GMOCK_HOME=/home/jeff/gmock-1.7.0
1
2
3
4
5
6
7
8
9
10
11
// Google Mock 使用示范
#include "gmock/gmock.h"
int main(int argc, char** argv) {
testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
// CppUTest 使用示范
#include "CppUTest/CommandLineTestRunner.h"
int main(int argc, char** argv) {
return CommandLineTestRunner::RunAllTests(argc, argv);
}
  • 下载 libCurl,并配置:export CURL_HOME=/home/jeff/curl-7.29.0
  • JsonCpp 需要 scons,下载:apt-get install scons
  • 下载 JsonCpp,并配置:export JSONCPP_HOME=/home/jeff/jsoncpp-src-0.5.0,其后在目录下执行 -> scons platform=linux-gcc,该命令创建 libjson_linux-gcc-4.9.1_libmt.a, 创建符号链接 -> ln -s libjson_linux-gcc-4.9.1_libmt.a libjson_linux-gcc-4.9.1.a
  • 下载 rlog (rlog 已废弃)
  • 下载并按照 Boost 库,配置:export BOOST_ROOT=/home/jeff/boost_1_57_0,export BOOST_VERSION=1.57.0

CppUTest TDD 框架补充

在 Makefile 增加以下路径 :
CPPFLAGS += -I(CPPUTEST_HOME)/include
CXXFLAGS += -include $(CPPUTEST_HOME)/include/CppUTest/MemoryLeakDetectorNewMacros.h
CFLAGS += -include $(CPPUTEST_HOME)/include/CppUTest/MemoryLeakDetectorMallocMacros.h
LD_LIBRARIES = -L$(CPPUTEST_HOME)/lib -lCppUTest -lCppUTestExt

  • 之后,便可开始第一个测试
1
2
3
4
5
6
TEST_GROUP(FirstTestGroup) {
};
//
TEST(FirstTestGroup, FirstTest) {
FAIL("Fail me!");
}