##C++ 基础
C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程 C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。
发布时间 | 文档 | 通称 | 备注 |
---|---|---|---|
2015 | ISO/IEC TS 19570:2015 | - | 用于并行计算的扩展 |
2015 | ISO/IEC TS 18822:2015 | - | 文件系统 |
2014 | ISO/IEC 14882:2014 | C++14 | 第四个C++标准 |
2011 | ISO/IEC TR 24733:2011 | - | 十进制浮点数扩展 |
2011 | ISO/IEC 14882:2011 | C++11 | 第三个C++标准 |
2010 | ISO/IEC TR 29124:2010 | - | 数学函数扩展 |
2007 | ISO/IEC TR 19768:2007 | C++TR1 | C++技术报告:库扩展 |
2006 | ISO/IEC TR 18015:2006 | - | C++性能技术报告 |
2003 | ISO/IEC 14882:2003 | C++03 | 第二个C++标准 |
1998 | ISO/IEC 14882:1998 | C++98 | 第一个C++标准 |
###类型
类型 | 关键字 |
---|---|
布尔型 | bool |
字符型 | char |
整型 | int |
浮点型 | float |
双浮点型 | double |
无类型 | void |
宽字符型 | wchar_t |
类型 | 位 | 范围 |
---|---|---|
char | 1 个字节 | -128 到 127 或者 0 到 255 |
unsigned char | 1 个字节 | 0 到 255 |
signed char | 1 个字节 | -128 到 127 |
int | 4 个字节 | -2147483648 到 2147483647 |
unsigned int | 4 个字节 | 0 到 4294967295 |
signed int | 4 个字节 | -2147483648 到 2147483647 |
short int | 2 个字节 | -32768 到 32767 |
unsigned short int | 2 个字节 | 0 到 65,535 |
signed short int | 2 个字节 | -32768 到 32767 |
long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
signed long int | 8 个字节 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
unsigned long int | 8 个字节 | 0 to 18,446,744,073,709,551,615 |
float | 4 个字节 | +/- 3.4e +/- 38 (~7 个数字) |
double | 8 个字节 | +/- 1.7e +/- 308 (~15 个数字) |
long double | 8 个字节 | +/- 1.7e +/- 308 (~15 个数字) |
wchar_t | 2 或 4 个字节 | 个宽字符 |
###typedef
使用 typedef
为一个已有的类型取一个新的名字。
typedef int feet;
###枚举
enum color { red=3, green, blue } c;
color cc = blue;
cout << cc << endl;
###变量
可以使用 extern
关键字在任何地方声明一个变量
extern int d = 3, f = 5; // d 和 f 的声明
int d = 3, f = 5; // 定义并初始化 d 和 f
###常量
- 使用
#define
预处理器 - 使用
const
关键字
#define LENGTH 10
const int LENGTH = 10;
###数组
double balance[10];
double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
double balance[] = {1000.0, 2.0, 3.4, 17.0, 50.0};
balance[4] = 50.0;
double salary = balance[9];
// 多维数组
int a[3][4] = {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
###判断
// if else
// 局部变量声明
int a = 100;
// 检查布尔条件
if( a < 20 )
{
// 如果条件为真,则输出下面的语句
cout << "a 小于 20" << endl;
}
else
{
// 如果条件为假,则输出下面的语句
cout << "a 大于 20" << endl;
}
// switch
// 局部变量声明
char grade = 'D';
switch(grade)
{
case 'A' :
cout << "很棒!" << endl;
break;
case 'B' :
case 'C' :
cout << "做得好" << endl;
break;
default :
cout << "无效的成绩" << endl;
}
cout << "您的成绩是 " << grade << endl;
###循环
-
break 终止循环,且程序流将继续执行紧接着循环的下一条语句。
-
break 用于终止 switch 语句中的一个 case。
-
continue 会跳过当前循环中的代码,强迫开始下一次循环。
// while 循环执行
int a = 10;
while( a < 20 )
{
cout << "a 的值:" << a << endl;
a++;
}
// do 循环执行
do
{
cout << "a 的值:" << a << endl;
a = a + 1;
}while( a < 20 );
// for 循环执行
for( int a = 10; a < 20; a = a + 1 )
{
cout << "a 的值:" << a << endl;
}
###运算符
算术运算符:+ - * / % ++ --
%
取模运算符,整除后的余数
关系运算符:== != > < >= <=
逻辑运算符:&& || !
位运算符:& | ^ ~ << >>
^
如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中~
二进制补码运算符是一元运算符,具有"翻转"位效果,即0变成1,1变成0。<<
二进制左移运算符,按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。左移n位就相当于乘以2的n次方。>>
二进制右移运算符,按二进制形式把所有的数字向左移动对应的位数,高位移出(舍弃),低位的空位补零。右移n位相当于除以2的n次方。
赋值运算符:= += -= *= /= %= <<= >>= &= ^= |=
###函数
函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。
####传值调用 传值调用方法,把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
// 声明
int maxNum(int num1, int num2);
// 传入2个int参数,其中一个有默认值,返回 int
int maxNum(int num1, int num2)
{
// 局部变量声明
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
####指针调用
指针调用方法,把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
// 函数定义
void swap(int *x, int *y)
{
int temp;
temp = *x; /* 保存地址 x 的值 */
*x = *y; /* 把 y 赋值给 x */
*y = temp; /* 把 x 赋值给 y */
return;
}
int main ()
{
// 局部变量声明
int a = 100;
int b = 200;
cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;
/* 调用函数来交换值
* &a 表示指向 a 的指针,即变量 a 的地址
* &b 表示指向 b 的指针,即变量 b 的地址
*/
swap(&a, &b);
cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;
return 0;
}
####引用调用
引用调用方法,把参数的地址复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
// 函数定义
void swap(int &x, int &y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
return;
}
int main ()
{
// 局部变量声明
int a = 100;
int b = 200;
cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;
/* 调用函数来交换值 */
swap(a, b);
cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;
return 0;
}
###Lambda 表达式
C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。 Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。
在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定
/*
[] // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
*/
[](int x, int y) -> int { int z = x + y; return z + x; }
###指针 *
每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。 通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值
所有指针的值的实际数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。 指针所指向的变量或常量的数据类型不同。
#include <iostream>
using namespace std;
int main ()
{
int var = 20; // 实际变量的声明
int *ip; // 指针变量的声明
int *ipNull = NULL; // 空指针
ip = &var; // 在指针变量中存储 var 的地址
cout << "var 变量的地址: " << &var << endl;
cout << "var 的值: ";
cout << var << endl;
cout << "在指针变量中存储的地址: ";
cout << ip << endl;
cout << "指针中地址的值: ";
cout << *ip << endl;
return 0;
}
###引用 &
- 不存在空引用。引用必须连接到一块合法的内存。
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
- 引用必须在创建时被初始化。指针可以在任何时间被初始化。
int i = 17;
int &r = i;
###输入输出
// 预定义的对象 cerr 是 ostream 类的一个实例。cerr 对象附属到标准错误设备,通常也是显示屏,但是 cerr 对象是非缓冲的,且每个流插入到 cerr 都会立即输出。
cerr << "错误 : cerr" << endl;
// 预定义的对象 clog 是 ostream 类的一个实例。clog 对象附属到标准错误设备,通常也是显示屏,但是 clog 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲在,直到缓冲填满或者缓冲区刷新时才会输出。
clog << "日志 : clog " << endl;
char name[50];
cout << "请输入您的名称: ";
// 预定义的对象 cin 是 istream 类的一个实例。cin 对象附属到标准输入设备,通常是键盘。cin 是与流提取运算符 >> 结合使用
cin >> name;
// 预定义的对象 cout 是 ostream 类的一个实例。cout 对象"连接"到标准输出设备,通常是显示屏。cout 是与流插入运算符 << 结合使用的
cout << "您的名称是: " << name << endl;
###数据结构(结构体)
#include <iostream>
#include <cstring>
using namespace std;
void printBook( struct Books book ); // 传值调用
void printBook2(struct Books *book); // 指针调用
// struct 语句定义了一个包含多个成员的新的数据类型
struct Books
{
char title[50];
int book_id;
};
int main( )
{
struct Books Book1; // 声明 Book1,类型为 Book
// Book1 详述
// strcpy(s1, s2) 复制字符串 s2 到字符串 s1。
strcpy( Book1.title, "Learn C++ Programming");
Book1.book_id = 6495407;
// 传值调用 输出 Book1 信息
printBook( Book1 );
// 指针调用
printBook2(&Book1);
return 0;
}
// 传值调用
void printBook( struct Books book )
{
cout << "Book title : " << book.title <<endl;
cout << "Book id : " << book.book_id <<endl;
}
// 指针调用
void printBook2(struct Books *book){
cout << "Book title : " << book->title <<endl;
cout << "Book id : " << book->book_id <<endl;
}
##面向对象
###类
C++ 在 C 语言的基础上增加了面向对象编程
####类的基本概念
#include <iostream>
#include <cstring>
using namespace std;
/*
* 类定义是以关键字 class 开头,后跟类的名称。
* 每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。
*/
class Box{
// 默认情况下,类的所有成员都是私有的,私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
private:
double *ptr; // 指针
double length; // 长度 私有
// 保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。
protected:
double breadth; // 宽度 受保护
// 关键字 public 确定了类成员的访问属性。在类对象作用域内,公共成员在类的外部是可访问的。
public:
Box(); // 构造函数
Box(double len); // 构造函数
Box(const Box &obj); // 拷贝构造函数 obj是一个对象引用,用于初始化另一个对象
~Box(); // 析构函数
friend void printLength(Box box); //友元函数
/*
* 静态成员
* 不能把静态成员放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化,
* 无论创建多少个类的对象,静态成员都只有一个副本。静态成员在类的所有对象中是共享的
*/
static int objectCount;
double height; // 高度
//成员函数可以定义在类定义内部,或者单独使用范围解析运算符 :: 来定义
void setLength(double len){
length = len;
}
double getLength(){
cout << "*ptr=len=" << *ptr << endl;
cout << "length=" << length << endl;
return length;
}
void setBreadth(double wid);
double getBreadth();
//返回体积
double getVolume(void)
{
// 每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。
cout << "getVolume 通过 this 调用 getBreadth = " << this->getBreadth() << endl;
return this->length * breadth * height;
}
};
/*
* 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
* 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
*/
Box::Box(void){
cout << "执行 构造函数..." << endl;
ptr = new double;
}
// Box::Box(double len) == Box::Box(double len): length(len)
Box::Box(double len){
cout << "执行 带参数的构造函数..." << endl;
length = len;
ptr = new double;
*ptr = len;
// 累加静态成员
objectCount += 1;
}
/*
* 拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象
* 用于:
* 1. 通过使用另一个同类型的对象来初始化新创建的对象。
* 2. 复制对象把它作为参数传递给函数。
* 3. 复制对象,并从函数返回这个对象。
*
* 拷贝构造函数 obj是一个对象引用,用于初始化另一个对象
*/
Box::Box(const Box &obj){
cout << "执行 拷贝构造函数..." << endl;
ptr = new double; // 为指针分配内存
*ptr = *obj.ptr;
}
/*
* 析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
* 析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
*/
Box::~Box(){
cout << "执行 析构函数..." << endl;
delete ptr;
}
// 成员函数可以定义在类定义内部,或者单独使用 范围解析运算符 :: 来定义
void Box::setBreadth(double wid){
breadth = wid;
}
double Box::getBreadth(){
return breadth;
}
/*
* 类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。
* 友元函数并不是成员函数。
* 友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。
* 友元类 = friend class ClassTwo;
*/
void printLength(Box box){
// 友元函数直接访问 私有成员
cout << "friend box.length=" << box.length << endl;
}
// 不能把静态成员放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化
int Box::objectCount = 1;
class OtherClass{
public:
/*
* C++ 内联函数是通常与类一起使用。
* 如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
* 在类定义中的定义的函数都是内联函数,即使没有使用 inline 说明符。
*/
inline int getSquare(int num){
return num * num;
}
};
/*
* 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
* 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
* 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
*/
// Box 子类
class SmallBox:public Box, public OtherClass{
public:
double getArea(){
return breadth * height;
}
};
int main( )
{
Box box1(6.0); // 声明 box1,类型为 Box,执行带参数的构造函数
box1.height = 5.0;
box1.setBreadth(7.0);
double volume = box1.height * box1.getLength() * box1.getBreadth();
cout << "box.成员 calculate volume=" << volume << endl;
cout << "box.getVolume=" << box1.getVolume() << endl;
// 使用友元函数
printLength(box1);
Box box2 = box1;
cout << "box2.getLength=" << box2.getLength() << " end."<< endl;
Box box3(1.0);// 目的是 使用构造函数累加 objectCount
// 静态成员在类的所有对象中是共享的
cout << "box.static.objectCount=" << Box::objectCount << endl;
SmallBox smallBox;
smallBox.height=2.0;
smallBox.setBreadth(3.0);
smallBox.setLength(4.0);
cout << "small box getVolume(box)=" << smallBox.getVolume() << endl;
cout << "small box getArea(self)=" << smallBox.getArea() << endl;
cout << "small box getSquare(OtherClass)=" << smallBox.getSquare(4) << endl;
// 指向类的指针
Box *ptrBox;
// 必须在使用指针之前,对指针进行初始化。
ptrBox = &box1;
cout << "ptrBox.volume" << ptrBox->getVolume() << endl;
return 0;
}
####继承、多态
#include <iostream>
using namespace std;
// 形状
class Shape{
protected:
double breadth;
double height;
public:
// 重载,函数同名,形式参数(指参数的个数、类型或者顺序)不同
void print(int i){
cout << "print int:" << i << endl;
}
void print(double d){
cout << "print double:" << d << endl;
}
// 重载 + 运算符
double operator+(const Shape &s){
return this->height + s.height;
}
// 重载 ++ 运算符
double operator++(){
return ++height;
}
// 使用友元函数 重载输出
friend ostream &operator<<( ostream &output, const Shape &s){
output << "height : " << s.height << " breadth : " << s.breadth;
return output;
}
void setHeight(double h);
double getHeight();
void setBreadth(double wid);
/*
* 虚函数
* 多态,子类重写该方法
* 虚函数 是在基类中使用关键字 virtual 声明的函数。
* 在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
* 如果没有 virtual 关键字,子类始终执行 父类的 area,就是 静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,
*/
virtual double area(){
cout << "执行 Shape.area" << endl;
return 0;
}
/*
* 纯虚函数(我理解就是抽象方法)
* 您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,
* 但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
*/
//virtual double area() = 0;
};
void Shape::setHeight(double h) {
height = h;
}
double Shape::getHeight() {
return height;
}
void Shape::setBreadth(double wid) {
breadth = wid;
}
// 三角形
class Triangle: public Shape{
public:
double area(){
cout << "执行 Triangle.area" << endl;
return (breadth * height)/2;
}
};
// 矩形
class Rectangle: public Shape{
public:
double area(){
cout << "执行 Rectangle.area" << endl;
return breadth * height;
}
};
int main(void){
Shape shape1;
// 重载方法
shape1.print(123);
shape1.print(1.23);
// 重载 输出运算符
cout << "重载输出:" << shape1 << endl;
shape1.setHeight(11);
shape1.setBreadth(12);
Shape shape2;
shape2.setHeight(22);
double height;
// 重载 + 运算符
height = shape1 + shape2;
cout << "重载 + 运算符:" << height << endl;
// 重载 ++ 运算符
cout << "重载 ++ 运算符:" << ++shape1 << endl;
// 多态
Shape *shape;
Triangle triangle;
Rectangle rectangle;
triangle.setBreadth(11);
triangle.setHeight(12);
rectangle.setBreadth(22);
rectangle.setHeight(23);
// 编译器看的是指针的内容,而不是它的类型。
shape = &rectangle;
shape->area();
shape = ▵
shape->area();
}
##进阶
###文件流
标准库 fstream
ofstream
该数据类型表示输出文件流,用于创建文件并向文件写入信息。ifstream
该数据类型表示输入文件流,用于从文件读取信息。fstream
该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。
open() 成员函数的第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。
ios::app
追加模式。所有写入都追加到文件末尾。ios::ate
文件打开后定位到文件末尾。ios::in
打开文件用于读取。ios::out
打开文件用于写入。ios::trunc
如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。
#include <iostream>
#include <fstream>
using namespace std;
int main(){
char data[100];
ofstream outfile;
// 以写模式打开文件
outfile.open("testFile.txt");
cout << "Writing to the file" << endl;
cout << "1.Enter : ";
// 获取输入内容
cin.getline(data,100);
// 向文件写入 输入的内容
outfile << data << endl;
cout << "2.Enter : ";
cin >> data;
cin.ignore();
// 向文件写入 输入的内容
outfile << data << endl;
// 关闭文件
outfile.close();
ifstream infile;
// 以读模式打开文件
infile.open("testFile.txt");
infile >> data;
cout << "Reading from the file" << endl;
cout << data << endl;
infile >> data;
cout << data << endl;
// 关闭文件
infile.close();
}
###异常处理
#include <iostream>
#include <exception>
using namespace std;
// 自定义异常
struct MyException: public exception{
// what() 是异常类提供的一个公共方法,它已被所有子异常类重载。这将返回异常产生的原因。
const char *what() const throw(){
return "my exception";
}
};
double division(int a,int b){
if(b==0){
// 抛出一个异常
throw "Division by zero condition";
}
return a/b;
}
int main(){
try{
division(12,0);
}catch (const char *msg){
cerr << msg << endl;
}
try {
throw MyException();
}catch(MyException &e){
std::cout << e.what() << std::endl;
}catch (std::exception &e){
std::cout << e.what() << std::endl;
}
return 0;
}
###内存
#include <iostream>
using namespace std;
int main(){
/*
* 栈:在函数内部声明的所有变量都将占用栈内存。
* 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
*/
// 初始化为 null 的指针
double* num = NULL;
// 建议检查 new 运算符是否返回 NULL 指针
// 使用 new 运算符来为任意的数据类型动态分配内存
// new 与 malloc() 函数相比,new 不只是分配了内存,它还创建了对象。
if( !(num = new double)){
cout << "error out of memory";
exit(1);
}
*num = 123.456;
cout << "double is " << *num << endl;
// 释放内存
delete num;
return 0;
}
###命名空间
#include <iostream>
/*
* using 指令也可以用来指定命名空间中的特定项目。
* 例如,如果您只打算使用 std 命名空间中的 cout 部分
* std 命名空间中的其他项目仍然需要加上命名空间名称作为前缀
*/
using std::cout;
// 命名空间,区分不同库中相同名称的函数、类、变量等。命名空间就是定义了一个范围。
namespace firstSpace{
void print(){
cout << "first space print" << std::endl;
}
}
namespace secondSpace{
void print(){
cout << "second space print" << std::endl;
}
}
/*
* 使用 using namespace 指令,在使用命名空间时就可以不用在前面加上命名空间的名称。
* 这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。
*/
using namespace firstSpace;
int main(){
// 使用 using namespace 指令,在使用命名空间时就可以不用在前面加上命名空间的名称
print();
// 调用带有命名空间的函数或变量,需要在前面加上命名空间的名称
secondSpace::print();
return 0;
}
###泛型
####函数
#include <iostream>
#include <string>
using namespace std;
// 函数模板
template <typename T>
inline T const& maxObj(T const& a, T const& b){
return a > b ? a:b;
}
int main(void){
cout << maxObj(12,23) << endl;
cout << maxObj("hello","world") << endl;
}
####类
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <stdexcept>
using namespace std;
// 类模板
template <class T>
class Stack{
private:
vector<T> elems; // 元素
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈 顶元素
bool empty() const{
// 是否为空
return elems.empty();
}
};
template <class T>
void Stack<T>::push(T const &elem) {
// 追加
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop() {
if (elems.empty()){
throw out_of_range("Stack<>::pop() is empty");
}
// 删除最后一个元素
elems.pop_back();
}
template <class T>
T Stack<T>::top() const const{
if (elems.empty()){
throw out_of_range("Stack<>::pop() is empty");
}
// 返回最后一个元素
return elems.back();
}
int main(void){
try{
Stack<int> intStack;
Stack<string> stringStack;
intStack.push(4);
cout << intStack.top() << endl;
stringStack.push("hello");
cout << stringStack.top() << std::endl;
stringStack.pop();
stringStack.pop();
}catch (exception const& ex){
cerr << "exception info = " << ex.what() << endl;
return -1;
}
return 0;
}
###预处理
预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。 所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。
#include <iostream>
using namespace std;
// #define 预处理指令用于创建符号常量。该符号常量通常称为宏
#define DEBUG true
// 定义一个带有参数的宏
#define MIN(a,b) ((a<b)?a: b)
// # 和 ## 预处理运算符在 C++ 和 ANSI/ISO C 中都是可用的。# 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。
#define MKSTR(x) #x
// ## 运算符用于连接两个令牌。
#define CONCAT(x, y) x ## y
int main(void){
//条件编译
#ifdef DEBUG
cerr << "current is debug" << endl;
#endif
// 可以使用 #if 0 语句注释掉程序的一部分
#if 0
cout << "不进行编译的代码" << endl;
#endif
// 带参数的宏
cout << MIN(22,11) << endl;
// # 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。
cout << MKSTR(HELLO C++) << endl;
// ## 运算符用于连接两个令牌。
cout << CONCAT(11, 22) << endl;
// 预定义宏
cout << "当前行号:" << __LINE__ << endl;
cout << "当前文件名:" << __FILE__ << endl;
cout << "当前日期:" << __DATE__ << endl;
cout << "当前时间:" << __TIME__ << endl;
return 0;
}
###信号处理(我的理解是swift中的通知)
#include <iostream>
#include <csignal>
using namespace std;
void signalHandler( int signum ) {
cout << "Interrupt signal (" << signum << ") received.\n";
exit(signum);
}
int main(void){
/*
* 以下 列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在 C++ 头文件 <csignal> 中。
* SIGABRT 程序的异常终止,如调用 abort。
* SIGFPE 错误的算术运算,比如除以零或导致溢出的操作。
* SIGILL 检测非法指令。
* SIGINT 接收到交互注意信号。
* SIGSEGV 非法访问内存。
* SIGTERM 发送到程序的终止请求。
*/
/*
* signal 函数,用来捕获突发事件
* 第一个参数是一个整数,代表了信号的编号;
* 第二个参数是一个指向信号处理函数的指针。
*/
signal(SIGINT,signalHandler);
int i = 0;
while(++i){
if (i == 3){
// 使用函数 raise() 生成信号
raise(SIGINT);
}
}
return 0;
}
###多线程
#include <iostream>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5
struct ThreadData{
int threadId;
char *message;
};
void* sayHello(void *args){
// 对传入的参数进行强制类型转换
struct ThreadData *threadData;
threadData = (struct ThreadData*) args;
cout << "hello threadId = " << threadData->threadId ;
cout << " message = " << threadData->message << endl;
// 在线程完成工作后无需继续存在时 调用pthread_exit退出线程
pthread_exit(NULL);
}
int main(void){
// 线程的 id 变量,多个变量使用数组
pthread_t threads[NUM_THREADS];
struct ThreadData threadData[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++){
cout << "for thread create i = " << i << endl;
threadData[i].threadId = i;
threadData[i].message = "this is thread message";
/*
* pthread_create 创建一个新的线程,并让它可执行
*
* thread 指向线程标识符指针。
* attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
* start_routine 线程运行函数起始地址,一旦线程被创建就会执行。
* arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。
*
* 通过结构传递多个参数。您可以在线程回调中传递任意的数据类型,因为它指向 void
*/
int thread = pthread_create(&threads[i], NULL, sayHello, (void *)&(threadData[i]));
if (thread != 0 ){
cout << "thread create error: " << thread << endl;
exit(-1);
}
}
/*
* pthread_exit 用于显式地退出一个线程。
* 通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。
*/
pthread_exit(NULL);
return 0;
}
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
#define NUM_THREADS 5
void* sayHello(void *args){
sleep(1);
// 对传入的参数进行强制类型转换
int threadId = *((int *)args);
cout << "hello thread id = " << threadId << endl;
// 在线程完成工作后无需继续存在时 调用pthread_exit退出线程
pthread_exit(NULL);
}
int main(void){
pthread_t threads[NUM_THREADS];
pthread_attr_t attr;
void *status;
// 初始化并设置线程为可连接的(joinable)
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
for (int i = 0; i< NUM_THREADS; i++){
cout << "for create thread = " << i << endl;
int thread = pthread_create(&threads[i], NULL, sayHello, (void *)&i);
if (thread){
cout << "for create thread error = " << thread << endl;
exit(-1);
}
}
// 删除属性
pthread_attr_destroy(&attr);
for (int i = 0 ; i< NUM_THREADS; i++){
/*
* pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。
* 当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。
* 只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。
* 使用 pthread_join() 函数来等待线程的完成
*/
int thread = pthread_join(threads[i], &status);
if (thread){
cout << "error unable join = " << thread << endl;
exit(-1);
}
cout << "for completed thread = " << i;
cout << " exiting with status = " << status << endl;
}
cout << "main completed" << endl;
return 0;
}
###CGI
未整理
###标准库
C++ 标准库包含了所有的 C 标准库,为了支持类型安全,做了一定的添加和修改。
####标准函数库
这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自 C 语言。
- 输入/输出 I/O
- 字符串和字符处理
- 数学
- 时间、日期和本地化
- 动态分配
- 其他
- 宽字符函数
####面向对象类库
这个库是类及其相关函数的集合。
- 标准的 C++ I/O 类
- String 类
- 数值类
- STL 容器类
- STL 算法
- STL 函数对象
- STL 迭代器
- STL 分配器
- 本地化库
- 异常处理类
- 杂项支持库
##个人补充
###->
和 .
->
前面是指针,.
前面是变量
###域运算符 ::
- 不能把静态成员放置在类的定义中,但是可以在类的外部通过使用
范围解析运算符 ::
来重新声明静态变量从而对它进行初始化, - 成员函数可以定义在类定义内部,或者在类外单独使用
范围解析运算符 ::
来定义 - 如果有个局部变量与全局变量同名
::a
可以使用全局变量 ::
只能访问静态成员函数std::cout
表示 cout 是在 std 中的变量using std::cout
只使用 std 命名空间中的 cout 部分 std 命名空间中的其他项目仍然需要加上命名空间名称作为前缀,如std::endl
::
左边一定是类名/命名空间,右边一定是变量名/函数名,左侧没有东西的时候,代表右侧的函数/变量是全局的
###*
指针 和 &
引用
&
在定义变量的时候是引用,起标识作用,如int &a = b;
a为b的一个引用, 引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名。 声明一个引用,不是新定义了一个变量,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。 不能建立数组的引用&
在表达式中为取地址,你的地址可以是任何类型 如int *a = &b;
a为指向b整型的一个指针*
是指针,是取地址的内容
###include ""
和 include <>
引号会先在程序所在的路径里找(自定义的库函数) 尖括号是从系统的头文件开始找(标准库函数)
有问题的地方,希望大家指出