【
--简介:C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。中级语言。 注意:使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查。
四大特性:封装、抽象、继承、抽象。
--标准库{
@1:核心语言,提供了所有构件块,包括变量、数据类型和常量,等等。
@2:C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
@3:标准模板库(STL),提供了大量的方法,用于操作数据结构等。
}
--C++的使用{
@1:C++ 通常用于编写设备驱动程序和其他要求实时性的直接操作硬件的软件。
@2:C++ 广泛用于教学和研究。
}
--C++环境设置{
@1: 文本编辑器
@2: C++编译器
}
--C++ 程序结构{
@1:#include <iostream>--定义了一些头文件,这些头文件包含了程序中必需的或有用的信息。
@2:using namespace std; 告诉编译器使用 std 命名空间。命名空间是 C++ 中一个相对新的概念。
@3: int main() 是主函数,程序从这里开始执行。
}
--C++关键字{
asm else new this auto enum operator throw bool explicit private true break export protected try
case extern public typedef catch false register typeid char float reinterpret_cast typename class for return union const friend short unsigned const_cast goto signed using continue if sizeof virtual default inline static void delete int static_cast volatile do long struct wchar_t double mutable switch while dynamic_cast namespace template
}
--数据类型{
@1、基本内置类型:布尔型(bool)、字符型(char)、整型(int)、浮点型(float)、双浮点型(double)、无类型(void)、宽字符型(wchar_t)-----类型修饰符:signed、unsigned、short、long
@2、类型取别名:typedef 声明---typedef type newname;
@3、枚举类型: enum 枚举名{ 标识符[=整型常数],.... }枚举变量;
}
--变量类型{
@1、变量定义:type variable_list;----- int i;
@2、声明:(变量声明)--extern int a, b; (函数声明)--int func();
}
--变量作用域{
@1、局部变量:
@2、形式参数:
@3、全局变量:
@4、初始化局部变量和全局变量:当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。定义全局变量时,系统会自动初始化为下列值:int(0)、char('\0')、float(0)、double(0)、pointer(NULL)
}
--常量{
@1、字符常量:\\(\)、\'(')、\"(")、\?(?)、\a(警报铃声)、\b(退格键)、\f(换页符)、\n(换行符)、\r(回车)、\t(水平制表符)、\v(垂直制表符)、\ooo(一到三位的八进制数)、\xhh . . .(一个或多个数字的十六进制数)
@2、定义常量:(使用 #define 预处理器:#define identifier value--#define LENGTH 10--area = LENGTH * WIDTH;
使用 const 关键字:const type variable = value;--const int LENGTH = 10;--area = LENGTH * WIDTH;)
}
--类型修饰符{
@1、类型修饰符:signed、unsigned、long、short。
@2、类型限制符:const(用于定义常量)、volatile(告诉编译器,变量的值可能以程序未明确指定的方式被改变。)、restrict(由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。只有 C99 增加了新的类型限定符 restrict。)
}
--存储类{
@1、说明:存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。auto(C++ 11,不再是。)、register(C++11,弃用)、static、extern、mutable、thread_local (C++11)。
@2、auto :声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。auto f=3.14; //double ---自动类型推断
@3、register :用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 '&' 运算符(因为它没有内存位置)。 register int miles;
@4、static :(static int count = 10; /* 全局变量 */--会使变量的作用域限制在声明它的文件内。 static int i = 5; // 局部静态变量--示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。)
@5、extern :用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 'extern' 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。extern 是用来在另一个文件中声明一个全局变量或函数;extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候。
@6、mutable :mutable 说明符仅适用于类的对象,它允许对象的成员替代常量,mutable 成员可以通过 const 成员函数修改。
@7、thread_local :声明的变量仅可在它在其上创建的线程上访问,变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。
}
--运算符{
@1、算术运算符:+、-、*、/、%、++、--
@2、关系运算符:==、!=、>、<、>=、<=
@3、逻辑运算符:&&、||、!
@4、位运算符:&、 | 和 ^(异或和&相反)、~(补码)、<<(左移乘2的n次方)、>>(右移)
@5、赋值运算符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=
@6、杂项运算符:sizeof(返回变量的大小--sizeof(a) )、Condition ? X : Y(条件运算符)、,(顺序运算符)、.(点)和 ->(箭头)(成员运算符)、Cast(强制转换--int(2.2000) 将返回 2)、&(指针运算符,返回变量地址--&a)、*(指针运算符,指向一个变量)
}
--循环{
@1、循环类型:while 循环、for 循环、do...while 循环、嵌套循环
@2、循环控制语句:break 语句、continue 语句、goto 语句(跳到标记处)
@3、无限循环:for( ; ; ){ }
}
--判断{
@1、判断语句:if 语句、if...else 语句、嵌套 if 语句、switch 语句、嵌套 switch 语句
@2、? : 运算符:Exp1 ? Exp2 : Exp3;
}
--函数{
@1、函数:函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。C++ 标准库提供了大量的程序可以调用的内置函数。
@2、定义函数:return_type function_name( parameter list ){ body of the function }
@3、函数定义:函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。--return_type function_name( parameter list );
@4、函数参数:形式参数,在函数的内部使用。(传值调用:对实参没影响。 指针调用:会影响实际参数。 引用调用:会影响实际参数。)
@5、参数默认值:为参数列表中后边的每一个参数指定默认值。当调用函数时,如果实际参数的值留空,则使用这个默认值。(如:int sum(int a, int b=20){ } )
}
--Lambda 函数与表达式{
@1、说明:C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。
Lambda 表达式本质上与函数声明非常类似。Lambda 表达式具体形式如下: [capture](parameters)->return-type{body}--[](int x, int y){ return x < y ; }
如果没有参数可以表示为:[capture](parameters){body} []{ ++global_x; }
@2在Lambda表达式内可以访问当前作用域的变量,这是Lambda表达式的闭包(Closure)行为。 与JavaScript闭包不同,C++变量传递有传值和传引用的区别。可以通过前面的[]来指定:
[] // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
另外有一点需要注意。对于[=]或[&]的形式,lambda 表达式可以直接使用 this 指针。但是,对于[]的形式,如果要使用 this 指针,必须显式传入:[this]() { this->someFunc(); }();
}
--数字{
@1、数学运算:使用内置函数,需要引入#include <cmath>。
double cos(double);----余弦
double sin(double);-----正弦
double tan(double);----正切
double log(double);----自然对数
double pow(double x, double y);-----x 的 y 次方
double hypot(double, double);-------两个参数的平方总和的平方根
double sqrt(double);----平方根
int abs(int);-------------绝对值
double fabs(double);----任意一个十进制数的绝对值
double floor(double);----返回一个小于或等于传入参数的最大整数
@2、随机数:(#include <iostream> #include <ctime> #include <cstdlib> ) 关于随机数生成器,有两个相关的函数。一个是 rand(),该函数只返回一个伪随机数。生成随机数之前必须先调用 srand() 函数。 如:(// 设置种子 srand( (unsigned)time( NULL ) ); // 生成实际的随机数-- j= rand(); )
}
--数组{
@1、说明:它可以存储一个固定大小的相同类型元素的顺序集合。数组中的特定元素可以通过索引访问。所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
@2、声明数组:需要指定元素的类型和元素的数量( type arrayName [ arraySize ]; )
@3、初始化数组: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};
@4、访问数组元素:double salary = balance[9];
@5、多维数组:type name[size1][size2]...[sizeN];---如:int threedim[5][10][4];
@6、指向数组的指针:数组名是一个指向数组中第一个元素的常量指针。如:double balance[50];--
balance
是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。
@7、传递数组给函数:
C++ 不允许向函数传递一个完整的数组作为参数,但是,您可以通过指定不带索引的数组名来传递一个指向数组的指针。
如果您想要在函数中传递一个一维数组作为参数,您必须以下面三种方式来声明函数形式参数:(--
形式参数是一个指针:
void myFunction(int *param){ } --
形式参数是一个已定义大小的数组:
void myFunction(int param[10]){} --
形式参数是一个未定义大小的数组:
void myFunction(int param[]){ }
)
@8、从函数返回数组:
C++ 不允许返回一个完整的数组作为函数的参数。但是,您可以通过指定不带索引的数组名来返回一个指向数组的指针。
如果您想要从函数返回一个一维数组,您必须声明一个返回指针的函数,如下:
int * myFunction(){ }
另外,C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
}
--字符串{
@1、形式:C 风格字符串 C++ 引入的 string 类类型
@2、C风格字符串:字符串实际上是使用 null 字符 '\0' 终止的一维字符数组。如:char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};依据数组初始化规则:char greeting[] = "Hello";
函数引用:#include <cstring>
strcpy(s1, s2);----复制字符串 s2 到字符串 s1。
strcat(s1, s2);-----连接字符串 s2 到字符串 s1 的末尾。
strlen(s1);---------返回字符串 s1 的长度。
strcmp(s1, s2);----如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。
strchr(s1, ch);-----返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
strstr(s1, s2);------返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
@3、C++ 中的 String 类:#include <string>(// 复制 str1 到 str3--str3 = str1; // 连接 str1 和 str2-- str3 = str1 + str2; // 连接后,str3 的总长度--len = str3.size();)
}
--指针{
@1、说明:还有一些任务,如动态内存分配,没有指针是无法执行的。可使用连字号(&)运算符访问的地址。
@2、什么是指针:指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。type *var-name; 都是一个代表内存地址的长的十六进制数。
@3、使用指针:int var = 20; int *ip; ip = &var; 地址:ip 地址的值:*ip
@4、Null指针:空指针,为指针变量赋一个 NULL 值。int *ptr = NULL;
@5、指针的算术运算:指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。
@6、指针 vs 数组:指针和数组是密切相关的。事实上,指针和数组在很多情况下是可以互换的。例如,一个指向数组开头的指针,可以通过使用指针的算术运算或数组索引来访问数组。*(var + 2) = 500;
@7、指针数组:如--int var[MAX] = {10, 100, 200}; int *ptr[MAX]; //指针数组 ptr[i] = &var[i]; // 赋值为整数的地址
//访问指针数组的值-- *ptr[i]
用一个指向字符的指针数组来存储一个字符串列表--const char *names[MAX] = {"Zara Ali","Hina Ali","Nuha Ali","Sara Ali"};
//访问:names[i]
@8、指向指针的指针(多级间接寻址):指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。
如:int **var;int **var; 示例:int var; int *ptr; int **pptr; // 获取 var 的地址 ptr = &var; // 使用运算符 & 获取 ptr 的地址 pptr = &ptr;
@9、传递指针给函数:传递指针给函数,只需要简单地声明函数参数为指针类型即可。 声明:void getSeconds(unsigned long *par); 调用: getSeconds( &sec );
@10、从函数返回指针:函数返回指针。为了做到这点,您必须声明一个返回指针的函数,如下所示:int * myFunction(){.....} C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
}
--引用{
@1、说明:引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。 int i = 17; // i为变量声明和定义 int& r = i; //声明引用变量(&读作引用)---就是给变量取别名。
@2、把引用作为参数:使用指针来实现引用调用函数。实例:// 函数声明 void swap(int& x, int& y); /* 调用函数来交换值 */---- swap(a, b);
@3、把引用作为返回值:通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。C++ 函数可以返回一个引用,方式与返回一个指针类似。当函数返回一个引用时,则返回一个指向返回值的隐式指针。实例:double& setValues( int i ){ return vals[i]; // 返回第 i 个元素的引用} //调用:setValues(1) = 20.23; // 改变第 2 个元素
当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。 int& func() { int q; //! return q; // 在编译时发生错误 static int x; return x; // 安全,x 在函数作用域外依然是有效的}
}
--日期&时间{
@1、说明:C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用 <ctime> 头文件。有四个与时间相关的类型:clock_t、time_t、size_t 和 tm。类型 clock_t、size_t 和 time_t 能够把系统时间和日期表示为某种整数。
结构类型 tm 把日期和时间以 C 结构的形式保存,tm 结构的定义如下:
struct tm {
int tm_sec; // 秒,正常范围从 0 到 59,但允许至 61
int tm_min; // 分,范围从 0 到 59
int tm_hour; // 小时,范围从 0 到 23
int tm_mday; // 一月中的第几天,范围从 1 到 31
int tm_mon; // 月,范围从 0 到 11
int tm_year; // 自 1900 年起的年数
int tm_wday; // 一周中的第几天,范围从 0 到 6,从星期日算起
int tm_yday; // 一年中的第几天,范围从 0 到 365,从 1 月 1 日算起
int tm_isdst; // 夏令时
}
@2、时间函数:
time_t time(time_t *time); --- 该函数返回系统的当前日历时间,自 1970 年 1 月 1 日以来经过的秒数。如果系统没有时间,则返回 .1。
char *ctime(const time_t *time); --- 该返回一个表示当地时间的字符串指针,字符串形式 day month year hours:minutes:seconds year\n\0。
struct tm *localtime(const time_t *time); --- 该函数返回一个指向表示本地时间的 tm 结构的指针。
clock_t clock(void); --- 该函数返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。如果时间不可用,则返回 .1。
char * asctime ( const struct tm * time ); --- 该函数返回一个指向字符串的指针,字符串包含了 time 所指向结构中存储的信息,返回形式为:day month date hours:minutes:seconds year\n\0。
struct tm *gmtime(const time_t *time); --- 该函数返回一个指向 time 的指针,time 为 tm 结构,用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示。
time_t mktime(struct tm *time); --- 该函数返回日历时间,相当于 time 所指向结构中存储的时间。
double difftime ( time_t time2, time_t time1 ); --- 该函数返回 time1 和 time2 之间相差的秒数。
size_t strftime(); --- 该函数可用于格式化日期和时间为指定的格式。
@3、当前日期和时间: // 基于当前系统的当前日期/时间 time_t now = time(0); // 把 now 转换为字符串形式 char* dt = ctime(&now); cout << "本地日期和时间:" << dt << endl;
// 把 now 转换为 tm 结构 tm *gmtm = gmtime(&now); dt = asctime(gmtm); cout << "UTC 日期和时间:"<< dt << endl;
@4、使用结构 tm 格式化时间:// 基于当前系统的当前日期/时间 time_t now = time(0); tm *ltm = localtime(&now);
// 输出 tm 结构的各个组成部分 cout << "年: "<< 1900 + ltm->tm_year << endl; cout << "月: "<< 1 + ltm->tm_mon<< endl; cout << "日: "<< ltm->tm_mday << endl; cout << "时间: "<< ltm->tm_hour << ":"; cout << ltm->tm_min << ":"; cout << ltm->tm_sec << endl;
}
--基本的输入输出{
@1、说明:C++ 的 I/O 发生在流中,流是字节序列。分为输入和输出流。
@2、I/O 库头文件:
<iostream>:该文件定义了 cin、cout、cerr 和 clog 对象,分别对应于标准输入流、标准输出流、非缓冲标准错误流和缓冲标准错误流。
<iomanip>:该文件通过所谓的参数化的流操纵器(比如 setw 和 setprecision),来声明对执行标准化 I/O 有用的服务。
<fstream>:该文件为用户控制的文件处理声明服务。我们将在文件和流的相关章节讨论它的细节。
@3、标准输出流(cout):预定义的对象 cout 是 ostream 类的一个实例。cout 对象"连接"到标准输出设备,通常是显示屏。cout 是与流插入运算符 << 结合使用的。如:cout << "Value of str is : " << str << endl;
@4、标准输入流(cin):预定义的对象 cin 是 istream 类的一个实例。cin 对象附属到标准输入设备,通常是键盘。cin 是与流提取运算符 >> 结合使用的。 如:cin >> name;
@5、标准错误流(cerr):预定义的对象 cerr 是 ostream 类的一个实例。cerr 对象附属到标准错误设备,通常也是显示屏,但是 cerr 对象是非缓冲的,且每个流插入到 cerr 都会立即输出。
cerr 也是与流插入运算符 << 结合使用的。
如:cerr << "Error message : " << str << endl;
@6、标准日志流(clog):预定义的对象 clog 是 ostream 类的一个实例。clog 对象附属到标准错误设备,通常也是显示屏,但是 clog 对象是缓冲的。这意味着每个流插入到 clog 都会先存储在缓冲在,直到缓冲填满或者缓冲区刷新时才会输出。
clog 也是与流插入运算符 << 结合使用的。 如:clog << "Error message : " << str << endl;
}
--数据结构<即结构体类型>{
@1、说明:C/C++ 数组允许定义可存储相同类型数据项的变量,但是结构是 C++ 中另一种用户自定义的可用的数据类型,它允许存储不同类型的数据项。
@2、定义结构:定义结构,必须使用 struct 语句。struct 语句定义了一个包含多个成员的新的数据类型。struct Books{char title[50]; char author[50]; char subject[100]; int book_id;} book;
@3、访问结构成员:为了访问结构的成员,我们使用成员访问运算符(.)。成员访问运算符是结构变量名称和我们要访问的结构成员之间的一个句号。如: Books Book1; // 定义结构体类型 Books 的变量 Book1 Books Book1;
// Book1 详述 strcpy( Book1.title, "C++ 教程"); Book1.book_id = 12345;
// 输出 Book1 信息 cout << "第二本书标题 : " << Book2.title <<endl; cout << "第二本书 ID : " << Book2.book_id <<endl;
@4、结构体作为函数参数:可以把结构作为函数参数,传参方式与其他类型的变量或指针类似。 如:void printBook( struct Books book ); 调用: printBook( Book1 );
@5、指向结构的指针:可以定义指向结构的指针,方式与定义指向其他类型变量的指针相似。struct Books *struct_pointer;
指针变量中存储结构变量的地址:struct_pointer = &Book1;
使用指向该结构的指针访问结构的成员,您必须使用 -> 运算符 struct_pointer->title;
@6、typedef 关键字:一种更简单的定义结构的方式,可以为创建的类型取一个"别名"。typedef struct{ char title[50]; char author[50]; char subject[100];int book_id;}Books; 引用:Books Book1, Book2;
}
--类和对象{
@1、类定义:定义一个类,本质上是定义一个数据类型的蓝图。 如:class Box{ public: double length; // 盒子的长度 double breadth; // 盒子的宽度 double height; // 盒子的高度 };
@2、定义对象:类提供了对象的蓝图,所以基本上,对象是根据类来创建的。声明类的对象,就像声明基本类型的变量一样。如:Box Box1; // 声明 Box1,类型为 Box
@3、访问数据成员: Box1.height = 5.0;
@4、类成员函数:类的成员函数是指那些把定义和原型写在类定义内部的函数,就像类定义中的其他变量一样。类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。class Box{ public: double length; double breadth; double height; double getVolume(void){ return length * breadth * height;} };
也可以在类的外部使用范围解析运算符 :: 定义该函数:double Box::getVolume(void){ return length * breadth * height; }
double Box::getVolume(void){ return length * breadth * height; }在这里,需要强调一点,在 :: 运算符之前必须使用类名。调用成员函数是在对象上使用点运算符(.),这样它就能操作与该对象相关的数据,如下所示:Box myBox; // 创建一个对象
myBox.getVolume(); // 调用该对象的成员函数
实例:// 成员函数声明 double getVolume(void); // 成员函数定义 double Box::getVolume(void){ return length * breadth * height;}
@5、类访问修饰符:public、private、protected 来指定。
@6、类构造函数 & 析构函数:类的构造函数:《类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。 如:class Line{ Line(); // 这是构造函数 }; // 成员函数定义,包括构造函数 Line::Line(void){ cout << "Object is being created" << endl;} 调用:Line line;》
带参数的构造函数:《class Line{ Line(double len); // 这是构造函数}; // 成员函数定义,包括构造函数Line::Line( double len){cout << "Object is being created, length = " << len << endl; length = len;} 调用:Line line(10.0); 》
使用初始化列表来初始化字段:《Line::Line( double len): length(len){ cout << "Object is being created, length = " << len << endl;} 等同于如下语法:Line::Line( double len){cout << "Object is being created, length = " << len << endl;length = len;}
如:C::C( double a, double b, double c): X(a), Y(b), Z(c){....} 》
类的析构函数:《类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。 如:class Line{ ~Line(); // 这是析构函数声明 }; Line::~Line(void){cout << "Object is being deleted" << endl;}》
@7、拷贝构造函数:拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于:通过使用另一个同类型的对象来初始化新创建的对象; 复制对象把它作为参数传递给函数; 复制对象,并从函数返回这个对象。
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:classname (const classname &obj) { // 构造函数的主体} 在这里,obj 是一个对象引用,该对象是用于初始化另一个对象的。
如:class Line{ Line( const Line &obj); // 拷贝构造函数}; Line::Line(const Line &obj){ cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl; ptr = new int; *ptr = *obj.ptr; // 拷贝值} void display(Line obj){cout << "line 大小 : " << obj.getLength() <<endl;} 调用: Line line(10); display(line);
@8、友元函数<friend>:友元函数可以访问类的 private 和 protected 成员。但是友元函数并不是成员函数。友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend。
如: friend void printWidth( Box box ); friend class ClassTwo;
实例:class Box{ public:friend void printWidth( Box box );}; // 请注意:printWidth() 不是任何类的成员函数 void printWidth( Box box ){/* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */ cout << "Width of box : " << box.width <<endl;} // 使用友元函数输出宽度 printWidth( box );
@9、内联函数< inline>:通过内联函数,编译器试图在调用函数的地方扩展函数体中的代码。
实例:inline int Max(int x, int y){return (x > y)? x : y;} cout << "Max (20,10): " << Max(20,10) << endl;
@10、C++ 中的 this 指针:每个对象都有一个特殊的指针 this,它指向对象本身。在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。 this->length = l;
@11、C++ 中指向类的指针:指向类的指针方式如同指向结构的指针。实际上,类可以看成是一个带有函数的结构。访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。
实例:Box *ptrBox; // 保存第一个对象的地址 ptrBox = &Box1; // 现在尝试使用成员访问运算符来访问成员 cout << "Volume of Box1: " << ptrBox->Volume() << endl;
@12、C++ 类的静态成员<static>:类的数据成员和函数成员都可以被声明为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。静态成员在类的所有对象中是共享的。不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化。
实例:class Box{ public: static int objectCount; }; // 初始化类 Box 的静态成员 int Box::objectCount = 0; // 输出对象的总数 cout << "Total objects: " << Box::objectCount << endl;
静态成员函数:《把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态函数只要使用类名加范围解析运算符 :: 就可以访问。静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。 静态成员函数与普通成员函数的区别:静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。 普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针。
实例:class Box{ static int getCount(){ return objectCount;}}; 调用:cout << "Inital Stage Count: " << Box::getCount() << endl;》
}
--继承{
@1、基类 & 派生类:形式如下:class derived-class: access-specifier base-class 如:// 基类 class Shape {};
// 派生类 class Rectangle: public Shape{};
@2、访问控制和继承:派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。 修饰符:public protected private
一个派生类继承了所有的基类方法,但下列情况除外:基类的构造函数、析构函数和拷贝构造函数; 基类的重载运算符; 基类的友元函数。
@3、继承类型:公有继承(public)、保护继承(protected)、私有继承(private)
@4、多继承:多继承即一个子类可以有多个父类,它继承了多个父类的特性。 C++ 类可以从多个类继承成员,语法如下: class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…{<派生类类体>};
实例:// 基类 Shape class Shape { }; // 基类 PaintCost class PaintCost { }; // 派生类class Rectangle: public Shape, public PaintCost{ };
}
--重载运算符和重载函数{
@1、说明:C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。选择最合适的重载函数或重载运算符的过程,称为重载决策。
@2、C++ 中的函数重载:在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。
实例: void print(int i) void print(double f) void print(char* c)
@3、C++ 中的运算符重载:可以重定义或重载大部分 C++ 内置的运算符。这样,就能使用自定义类型的运算符。重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。
如:Box operator+(const Box&); Box operator+(const Box&, const Box&);
实例:// 重载 + 运算符,用于把两个 Box 对象相加Box operator+(const Box& b){ Box box; box.length = this->length + b.length; box.breadth = this->breadth + b.breadth; box.height = this->height + b.height; return box;}
调用: // 把两个对象相加,得到 Box3 Box3 = Box1 + Box2;
--可重载运算符:《==双目算术运算符 + (加),-(减),*(乘),/(除),% (取模) ==关系运算符 ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于) ==逻辑运算符 ||(逻辑或),&&(逻辑与),!(逻辑非) ==单目运算符 + (正),-(负),*(指针),&(取地址) ==自增自减运算符 ++(自增),--(自减) ==位运算符 | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移) ==赋值运算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>= ==空间申请与释放 new, delete, new[ ] , delete[] ==其他运算符 ()(函数调用),->(成员访问),->*(成员指针访问),,(逗号),[](下标)》
--不可重载运算符:《==.:成员访问运算符 ==.*, ->*:成员指针访问运算符 ==:::域运算符 ==sizeof:长度运算符
==?::条件运算符 ==#: 预处理符号》
}
--多态{
@1、多态<动态链接>:多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。声明前放置关键字 virtual。virtual int area(){} 这就是多态的一般使用方式。有了多态,可以有多个不同的类,都带有同一个名称但具有不同实现的函数,函数的参数甚至可以是相同的。
@2、虚函数:虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。
@3、纯虚函数:可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。 如: // pure virtual function virtual int area() = 0; = 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数。
}
--数据抽象{
@1、数据抽象:数据抽象是指,只向外界提供关键信息,并隐藏其后台的实现细节,即只表现必要的信息而不呈现细节。 数据抽象是一种依赖于接口和实现分离的编程(设计)技术。 C++ 编程而言,C++ 类为数据抽象提供了可能。它们向外界提供了大量用于操作对象数据的公共方法,也就是说,外界实际上并不清楚类的内部实现。
@2、访问标签强制抽象:在 C++ 中,我们使用访问标签来定义类的抽象接口。一个类可以包含零个或多个访问标签。
@3、数据抽象的好处:类的内部受到保护,不会因无意的用户级错误导致对象状态受损。 类实现可能随着时间的推移而发生变化,以便应对不断变化的需求,或者应对那些要求不改变用户级代码的错误报告。
@4、数据抽象的实例:C++ 程序中,任何带有公有和私有成员的类都可以作为数据抽象的实例。 即在类中定义私有成员变量,对外开发公共方法。
@5、设计策略:抽象把代码分离为接口和实现。所以在设计组件时,必须保持接口独立于实现,这样,如果改变底层实现,接口也将保持不变。在这种情况下,不管任何程序使用接口,接口都不会受到影响,只需要将最新的实现重新编译即可。
}
--数据封装{
@1、数据封装:两个基本要素,程序语句(代码):这是程序中执行动作的部分,它们被称为函数。程序数据:数据是程序的信息,会受到程序函数的影响。 ==封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念,这样能避免受到外界的干扰和误用,从而确保了安全。数据封装引申出了另一个重要的 OOP 概念,即数据隐藏。 ==数据封装是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。 ==C++ 通过创建类来支持封装和数据隐藏(public、protected、private)。 ==通过私有成员,公共开放方法实现数据封装。
@2、设计策略:通常情况下,都会设置类成员状态为私有(private),除非我们真的需要将其暴露,这样才能保证良好的封装性。这通常应用于数据成员,但它同样适用于所有成员,包括虚函数。
}
--接口(抽象类){
@1、接口(抽象类):接口描述了类的行为和功能,而不需要完成类的特定实现。 ==C++ 接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念。 ==如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 "= 0" 来指定的:public: virtual double getVolume() = 0; // 纯虚函数
==设计抽象类(通常称为 ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。抽象类不能被用于实例化对象,它只能作为接口使用。
==如果一个 ABC 的子类需要被实例化,则必须实现每个虚函数,这也意味着 C++ 支持使用 ABC 声明接口。如果没有在派生类中重载纯虚函数,就尝试实例化该类的对象,会导致编译错误。
==可用于实例化对象的类被称为具体类。
@2、抽象类的实例:// 基类-为抽象类 class Shape { public: virtual int getArea() = 0; // 提供接口框架的纯虚函数};
// 派生类 class Rectangle: public Shape{ //要实现抽象类中的虚函数(包括纯虚函数)};
@3、设计策略:面向对象的系统可能会使用一个抽象基类为所有的外部应用程序提供一个适当的、通用的、标准化的接口。然后,派生类通过继承抽象基类,就把所有类似的操作都继承下来。
==外部应用程序提供的功能(即公有函数)在抽象基类中是以纯虚函数的形式存在的。这些纯虚函数在相应的派生类中被实现。这个架构也使得新的应用程序可以很容易地被添加到系统中,即使是在系统被定义之后依然可以如此。
}
--文件和流{
@1、文件和流: ==ofstream:该数据类型表示输出文件流,用于创建文件并向文件写入信息。 ==ifstream:该数据类型表示输入文件流,用于从文件读取信息。 ==fstream:该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。
@2、打开文件:在从文件读取信息或者向文件写入信息之前,必须先打开文件。ofstream 和 fstream 对象都可以用来打开文件进行写操作,如果只需要打开文件进行读操作,则使用 ifstream 对象。 == open() 函数的标准语法,open() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。 ==void open(const char *filename, ios::openmode mode);---open() 成员函数的第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。《ios::app--追加模式。所有写入都追加到文件末尾。 ios::ate--文件打开后定位到文件末尾。 ios::in--打开文件用于读取。 ios::out--打开文件用于写入。 ios::trunc--如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。 ==可以把以上两种或两种以上的模式结合使用:ofstream outfile; outfile.open("file.dat", ios::out | ios::trunc );》
@3、关闭文件:当 C++ 程序终止时,它会自动关闭刷新所有流,释放所有分配的内存,并关闭所有打开的文件。但程序员应该养成一个好习惯,在程序终止前关闭所有打开的文件。 ==下面是 close() 函数的标准语法,close() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。----void close();
@4、写入文件:在 C++ 编程中,我们使用流插入运算符( << )向文件写入信息,就像使用该运算符输出信息到屏幕上一样。唯一不同的是,在这里您使用的是 ofstream 或 fstream 对象,而不是 cout 对象。
@5、读取文件:在 C++ 编程中,我们使用流提取运算符( >> )从文件读取信息,就像使用该运算符从键盘输入信息一样。唯一不同的是,在这里您使用的是 ifstream 或 fstream 对象,而不是 cin 对象。
@6、读取 & 写入实例: char data[100]; // 以写模式打开文件 ofstream outfile; outfile.open("afile.dat");
// 向文件写入用户输入的数据 outfile << data << endl; // 关闭打开的文件 outfile.close();
// 以读模式打开文件 ifstream infile; infile.open("afile.dat");
// 从文件读取数据 infile >> data; // 关闭打开的文件 infile.close();
==实例中使用了 cin 对象的附加函数,比如 getline()函数从外部读取一行,ignore() 函数会忽略掉之前读语句留下的多余字符。
@7、文件位置指针:istream 和 ostream 都提供了用于重新定位文件位置指针的成员函数。这些成员函数包括关于 istream 的 seekg("seek get")和关于 ostream 的 seekp("seek put")。 ==seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。查找方向可以是 ios::beg(默认的,从流的开头开始定位),也可以是 ios::cur(从流的当前位置开始定位),也可以是 ios::end(从流的末尾开始定位)。 ==文件位置指针是一个整数值,指定了从文件的起始位置到指针所在位置的字节数。 // 定位到 fileObject 的第 n 个字节(假设是 ios::beg) fileObject.seekg( n );
==// 把文件的读指针从 fileObject 当前位置向后移 n 个字节 fileObject.seekg( n, ios::cur );
==// 把文件的读指针从 fileObject 末尾往回移 n 个字节 fileObject.seekg( n, ios::end );
==// 定位到 fileObject 的末尾 fileObject.seekg( 0, ios::end );
}
--文件和流{
@1、文件和流: ==ofstream:该数据类型表示输出文件流,用于创建文件并向文件写入信息。 ==ifstream:该数据类型表示输入文件流,用于从文件读取信息。 ==fstream:该数据类型通常表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。
@2、打开文件:在从文件读取信息或者向文件写入信息之前,必须先打开文件。ofstream 和 fstream 对象都可以用来打开文件进行写操作,如果只需要打开文件进行读操作,则使用 ifstream 对象。 == open() 函数的标准语法,open() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。 ==void open(const char *filename, ios::openmode mode);---open() 成员函数的第一参数指定要打开的文件的名称和位置,第二个参数定义文件被打开的模式。《ios::app--追加模式。所有写入都追加到文件末尾。 ios::ate--文件打开后定位到文件末尾。 ios::in--打开文件用于读取。 ios::out--打开文件用于写入。 ios::trunc--如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设为 0。 ==可以把以上两种或两种以上的模式结合使用:ofstream outfile; outfile.open("file.dat", ios::out | ios::trunc );》
@3、关闭文件:当 C++ 程序终止时,它会自动关闭刷新所有流,释放所有分配的内存,并关闭所有打开的文件。但程序员应该养成一个好习惯,在程序终止前关闭所有打开的文件。 ==下面是 close() 函数的标准语法,close() 函数是 fstream、ifstream 和 ofstream 对象的一个成员。----void close();
@4、写入文件:在 C++ 编程中,我们使用流插入运算符( << )向文件写入信息,就像使用该运算符输出信息到屏幕上一样。唯一不同的是,在这里您使用的是 ofstream 或 fstream 对象,而不是 cout 对象。
@5、读取文件:在 C++ 编程中,我们使用流提取运算符( >> )从文件读取信息,就像使用该运算符从键盘输入信息一样。唯一不同的是,在这里您使用的是 ifstream 或 fstream 对象,而不是 cin 对象。
@6、读取 & 写入实例: char data[100]; // 以写模式打开文件 ofstream outfile; outfile.open("afile.dat");
// 向文件写入用户输入的数据 outfile << data << endl; // 关闭打开的文件 outfile.close();
// 以读模式打开文件 ifstream infile; infile.open("afile.dat");
// 从文件读取数据 infile >> data; // 关闭打开的文件 infile.close();
==实例中使用了 cin 对象的附加函数,比如 getline()函数从外部读取一行,ignore() 函数会忽略掉之前读语句留下的多余字符。
@7、文件位置指针:istream 和 ostream 都提供了用于重新定位文件位置指针的成员函数。这些成员函数包括关于 istream 的 seekg("seek get")和关于 ostream 的 seekp("seek put")。 ==seekg 和 seekp 的参数通常是一个长整型。第二个参数可以用于指定查找方向。查找方向可以是 ios::beg(默认的,从流的开头开始定位),也可以是 ios::cur(从流的当前位置开始定位),也可以是 ios::end(从流的末尾开始定位)。 ==文件位置指针是一个整数值,指定了从文件的起始位置到指针所在位置的字节数。 // 定位到 fileObject 的第 n 个字节(假设是 ios::beg) fileObject.seekg( n );
==// 把文件的读指针从 fileObject 当前位置向后移 n 个字节 fileObject.seekg( n, ios::cur );
==// 把文件的读指针从 fileObject 末尾往回移 n 个字节 fileObject.seekg( n, ios::end );
==// 定位到 fileObject 的末尾 fileObject.seekg( 0, ios::end );
}
--动态内存
{
@1、动态内存: 栈:在函数内部声明的所有变量都将占用栈内存。 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。 ==在 C++ 中,可以使用特殊的运算符为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。这种运算符即 new 运算符。 ==如果不需要动态分配内存,可以使用 delete 运算符,删除之前由 new 运算符分配的内存。
@2、new 和 delete 运算符: 使用 new 运算符来为任意的数据类型动态分配内存的通用语法:new data-type; 如:double* pvalue = NULL; // 初始化为 null 的指针 pvalue = new double; // 为变量请求内存 ==malloc() 函数在 C 语言中就出现了,在 C++ 中仍然存在,但建议尽量不要使用 malloc() 函数。new 与 malloc() 函数相比,其主要的优点是,new 不只是分配了内存,它还创建了对象。
==在任何时候,当觉得某个已经动态分配内存的变量不再需要使用时,可以使用 delete 操作符释放它所占用的内存:delete pvalue; // 释放 pvalue 所指向的内存。
@3、数组的动态内存分配:// 动态分配,数组长度为 m int *array=new int [m]; //释放内 delete [] array;
@4、对象的动态内存分配:Box* myBoxArray = new Box[4]; delete [] myBoxArray; // 删除数组 --对象会被创建4次会被销毁4次。
}
--命名空间{
@1、命名空间:引入了命名空间这个概念,用于解决不同文件重名的现象,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。 ==使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
@2、定义命名空间:命名空间的定义使用关键字 namespace,后跟命名空间的名称,如下所示:namespace namespace_name { // 代码声明}
==为了调用带有命名空间的函数或变量,需要在前面加上命名空间的名称,如下所示:name::code; // code 可以是变量或函数
==实例:// 第一个命名空间 namespace first_space{} // 调用第一个命名空间中的函数 first_space::func();
@3、using 指令:可以使用 using namespace 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。 ==using namespace first_space; // 调用第一个命名空间中的函数 func();
==using 指令也可以用来指定命名空间中的特定项目。例如,如果您只打算使用 std 命名空间中的 cout 部分,您可以使用如下的语句:using std::cout;
@4、嵌套命名空间:命名空间可以嵌套,可以在一个命名空间中定义另一个命名空间--namespace namespace_name1 {// 代码声明 namespace namespace_name2 { // 代码声明}}
==引用--可以通过使用 :: 运算符来访问嵌套的命名空间中的成员:// 访问 namespace_name2 中的成员 using namespace namespace_name1::namespace_name2; // 访问 namespace:name1 中的成员 using namespace namespace_name1;
}
--模板{
@1、模板:模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。 ==模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。 ==每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector <int> 或 vector <string>。
@2、函数模板:一般形式:template <class type> ret-type func-name(parameter list){// 函数的主体}
==实例如:template <typename T> inline T const& Max (T const& a, T const& b) { return a < b ? b:a; }
=调用: cout << "Max(i, j): " << Max(20, 30) << endl;
@3、类模板:泛型类声明的一般形式如下所示:template <class type> class class-name { ... }
==实例如: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); }
=调用: Stack<int> intStack; // int 类型的栈 Stack<string> stringStack; // string 类型的栈 // 操作 int 类型的栈 intStack.push(7); cout << intStack.top() <<endl;
}
--预处理器{
@1、预处理器:预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。 ==以井号开头,预处理指令不是 C++ 语句,不会以分号(;)结尾。 ==#include 指令,这个宏用于把头文件包含到源文件中。 ==C++ 还支持很多预处理指令,比如 #include、#define、#if、#else、#line 等。
@2、#define 预处理:#define 预处理指令用于创建符号常量。该符号常量通常称为宏,指令的一般形式是:#define macro-name replacement-text 实例如:#define PI 3.14159 使用:cout << "Value of PI :" << PI << endl;
@3、参数宏:可以使用 #define 来定义一个带有参数的宏 ==实例:#define MIN(a,b) (a<b ? a : b) cout <<"较小的值为:" << MIN(i, j) << endl;
@4、条件编译:有几个指令可以用来有选择地对部分程序源代码进行编译。这个过程被称为条件编译。条件预处理器的结构与 if 选择结构很像。#ifndef NULL #define NULL 0 #endif
==实例:#ifdef DEBUG cerr <<"Variable x = " << x << endl; #endif #if 0 不进行编译的代码 #endif
@5、# 和 ## 运算符:# 和 ## 预处理运算符在 C++ 和 ANSI/ISO C 中都是可用的。# 运算符会把 replacement-text 令牌转换为用引号引起来的字符串。 实例:#define MKSTR( x ) #x cout << MKSTR(HELLO C++) << endl; 说明:C++ 预处理器把下面这行:cout << MKSTR(HELLO C++) << endl; 转换成了:cout << "HELLO C++" << endl;
==## 运算符用于连接两个令牌。 实例:#define CONCAT( x, y ) x ## y
=当 CONCAT 出现在程序中时,它的参数会被连接起来,并用来取代宏。例如,程序中 CONCAT(HELLO, C++) 会被替换为 "HELLO C++",如下面实例所示。#define concat(a, b) a ## b 调用:cout << concat(10, 888);:
@6、C++ 中的预定义宏:《__LINE__ 这会在程序编译时包含当前行号。
__FILE__ 这会在程序编译时包含当前文件名。
__DATE__ 这会包含一个形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。
__TIME__ 这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。
》
}
--信号处理{
@1、信号处理:信号是由操作系统传给进程的中断,会提早终止一个程序。可以通过按 Ctrl+C 产生中断。
==有些信号不能被程序捕获,但是下表所列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在 C++ 头文件 <csignal> 中。《SIGABRT----程序的异常终止,如调用 abort。
SIGFPE----错误的算术运算,比如除以零或导致溢出的操作。
SIGILL-----检测非法指令。
SIGINT----接收到交互注意信号。
SIGSEGV--非法访问内存。
SIGTERM--发送到程序的终止请求。》
@2、signal() 函数:C++ 信号处理库提供了 signal 函数,用来捕获突发事件。以下是 signal() 函数的语法:void (*signal (int sig, void (*func)(int)))(int); --这个函数接收两个参数:第一个参数是一个整数,代表了信号的编号;第二个参数是一个指向信号处理函数的指针。 ==不管您想在程序中捕获什么信号,您都必须使用 signal 函数来注册信号,并将其与信号处理程序相关联。
==实例: // 注册信号 SIGINT 和信号处理程序 signal(SIGINT, signalHandler);
@3、raise() 函数:可以使用函数 raise() 生成信号,该函数带有一个整数信号编号作为参数,语法如下:int raise (signal sig);sig 是要发送的信号的编号,这些信号包括:SIGINT、SIGABRT、SIGFPE、SIGILL、SIGSEGV、SIGTERM、SIGHUP。--- raise( SIGINT);
}
--多线程{
@1、多线程:多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。《基于进程的多任务处理是程序的并发执行。 基于线程的多任务处理是同一程序的片段的并发执行。》
@2、创建线程:可以用它来创建一个 POSIX 线程:#include <pthread.h> pthread_create (thread, attr, start_routine, arg)
参数说明:《thread ---- 指向线程标识符指针。 attr-----一个不透明的属性对象,可以被用来设置线程属性。可以指定线程属性对象,也可以使用默认值 NULL。 start_routine----线程运行函数起始地址,一旦线程被创建就会执行。 arg----运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。》
==创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。
@3、终止线程:可以用它来终止一个 POSIX 线程:#include <pthread.h> pthread_exit (status) pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。
==如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。
@4、实例:// 必须的头文件是 #include <pthread.h> // 定义线程的 id 变量,多个变量使用数组 pthread_t tids[5]; for(int i = 0; i < 5; ++i){//参数依次是:创建的线程id,线程参数,调用的函数,传入的函数参数
int ret = pthread_create(&tids[i], NULL, say_hello, NULL);}
pthread_exit(NULL); //等各个线程退出后,进程才结束,否则进程强制结束了,线程可能还没反应过来;
@5、向线程传递参数: rc = pthread_create(&threads[i], NULL,PrintHello, (void *)&td[i]);
@6、连接和分离线程:pthread_join (threadid, status) pthread_detach (threadid)
==pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连接。 实例:// 初始化并设置线程为可连接的(joinable) pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
// 删除属性,并等待其他线程 pthread_attr_destroy(&attr); rc = pthread_join(threads[i], &status);
}
--Web编程{
@1、什么是 CGI?:《公共网关接口(CGI),是一套标准,定义了信息是如何在 Web 服务器和客户端脚本之间进行交换的。 ==CGI 规范目前是由 NCSA 维护的,NCSA 定义 CGI 如下:
=公共网关接口(CGI),是一种用于外部网关程序与信息服务器(如 HTTP 服务器)对接的接口标准。
=目前的版本是 CGI/1.1,CGI/1.2 版本正在推进中。》
@2、Web 浏览:为了更好地了解 CGI 的概念,让我们点击一个超链接,浏览一个特定的网页或 URL,看看会发生什么。《
=浏览器联系上 HTTP Web 服务器,并请求 URL,即文件名。
=Web 服务器将解析 URL,并查找文件名。如果找到请求的文件,Web 服务器会把文件发送回浏览器,否则发送一条错误消息,表明您请求了一个错误的文件。
=Web 浏览器从 Web 服务器获取响应,并根据接收到的响应来显示文件或错误消息。
=然而,以这种方式搭建起来的 HTTP 服务器,不管何时请求目录中的某个文件,HTTP 服务器发送回来的不是该文件,而是以程序形式执行,并把执行产生的输出发送回浏览器显示出来。
=公共网关接口(CGI),是使得应用程序(称为 CGI 程序或 CGI 脚本)能够与 Web 服务器以及客户端进行交互的标准协议。这些 CGI 程序可以用 Python、PERL、Shell、C 或 C++ 等进行编写。》
@3、CGI 架构图:(Web Client)--->(HTTP Protocol)--->(Web Server)<--->(Server Side Script)<--->(Database)--->(Web Client)
@4、Web 服务器配置:《进行 CGI 编程之前,请确保的 Web 服务器支持 CGI,并已配置成可以处理 CGI 程序。所有由 HTTP 服务器执行的 CGI 程序,都必须在预配置的目录中。该目录称为 CGI 目录,按照惯例命名为 /var/www/cgi-bin。虽然 CGI 文件是 C++ 可执行文件,但是按照惯例它的扩展名是 .cgi。
==默认情况下,Apache Web 服务器会配置在 /var/www/cgi-bin 中运行 CGI 程序。如果想指定其他目录来运行 CGI 脚本,您可以在 httpd.conf 文件中修改以下部分:<Directory "/var/www/cgi-bin"> AllowOverride None Options ExecCGI Order allow,deny Allow from all</Directory>
<Directory "/var/www/cgi-bin"> Options All </Directory>》
@5、第一个 CGI 程序:《int main (){ cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>Hello World - 第一个 CGI 程序</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<h2>Hello World! 这是我的第一个 CGI 程序</h2>\n"; cout << "</body>\n"; cout << "</html>\n"; return 0;}》
@6、HTTP 头信息:《行 Content-type:text/html\r\n\r\n 是 HTTP 头信息的组成部分,它被发送到浏览器,以便更好地理解页面内容。HTTP 头信息的形式如下:HTTP 字段名称: 字段内容 例如 Content-type: text/html\r\n\r\n
一些其他的重要的 HTTP 头信息:
==Content-type: MIME 字符串,定义返回的文件格式。例如 Content-type:text/html。
==Expires: Date 信息变成无效的日期。浏览器使用它来判断一个页面何时需要刷新。一个有效的日期字符串的格式应为 01 Jan 1998 12:00:00 GMT。
==Location: URL 这个 URL 是指应该返回的 URL,而不是请求的 URL。你可以使用它来重定向一个请求到任意的文件。
==Last-modified: Date 资源的最后修改日期。
==Content-length: N 要返回的数据的长度,以字节为单位。浏览器使用这个值来表示一个文件的预计下载时间。
==Set-Cookie: String 通过 string 设置 cookie。
》
}
@7、CGI 环境变量:所有的 CGI 程序都可以访问下列的环境变量。这些变量在编写 CGI 程序时扮演了非常重要的角色。《
==CONTENT_TYPE 内容的数据类型。当客户端向服务器发送附加内容时使用。例如,文件上传等功能。
==CONTENT_LENGTH 查询的信息长度。只对 POST 请求可用。
==HTTP_COOKIE 以键 & 值对的形式返回设置的 cookies。
==HTTP_USER_AGENT 用户代理请求标头字段,递交用户发起请求的有关信息,包含了浏览器的名称、版本和其他平台性的附加信息。
==PATH_INFO CGI 脚本的路径。
==QUERY_STRING 通过 GET 方法发送请求时的 URL 编码信息,包含 URL 中问号后面的参数。
==REMOTE_ADDR 发出请求的远程主机的 IP 地址。这在日志记录和认证时是非常有用的。
==REMOTE_HOST 发出请求的主机的完全限定名称。如果此信息不可用,则可以用 REMOTE_ADDR 来获取 IP 地址。
==REQUEST_METHOD 用于发出请求的方法。最常见的方法是 GET 和 POST。
==SCRIPT_FILENAME CGI 脚本的完整路径。
==SCRIPT_NAME CGI 脚本的名称。
==SERVER_NAME 服务器的主机名或 IP 地址。
==SERVER_SOFTWARE 服务器上运行的软件的名称和版本。
》
@8、使用 GET 方法传递信息:GET 方法发送已编码的用户信息追加到页面请求中。页面和已编码信息通过 ? 字符分隔开,如下所示:
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
GET 方法有大小限制,在一个请求字符串中最多可以传 1024 个字符。
@9、简单的表单实例:GET 方法:<form action="/cgi-bin/cpp_get.cgi" method="get">...</form>
@10、使用 POST 方法传递信息:一个更可靠的向 CGI 程序传递信息的方法是 POST 方法。这种方法打包信息的方式与 GET 方法相同,不同的是,它不是把信息以文本字符串形式放在 URL 中的 ? 之后进行传递,而是把它以单独的消息形式进行传递。该消息是以标准输入的形式传给 CGI 脚本的。
==<form action="/cgi-bin/cpp_get.cgi" method="post">...</form>
@11、向 CGI 程序传递复选框数据:<input type="checkbox" name="maths" value="on" /> 数学
@12、向 CGI 程序传递单选按钮数据:<input type="radio" name="subject" value="maths" checked="checked"/> 数学
@13、向 CGI 程序传递文本区域数据:<textarea name="textcontent" cols="40" rows="4">请在这里输入文本...</textarea>
@14、向 CGI 程序传递下拉框数据:<select name="dropdown"> <option value="Maths" selected>数学</option> <option value="Physics">物理</option></select>
@15、在 CGI 中使用 Cookies:HTTP 协议是一种无状态的协议。但对于一个商业网站,它需要在不同页面间保持会话信息。例如,一个用户在完成多个页面的步骤之后结束注册。但是,如何在所有网页中保持用户的会话信息。在许多情况下,使用 cookies 是记忆和跟踪有关用户喜好、购买、佣金以及其他为追求更好的游客体验或网站统计所需信息的最有效的方法。
==它是如何工作的:服务器以 cookie 的形式向访客的浏览器发送一些数据。如果浏览器接受了 cookie,则 cookie 会以纯文本记录的形式存储在访客的硬盘上。现在,当访客访问网站上的另一个页面时,会检索 cookie。一旦找到 cookie,服务器就知道存储了什么。cookie 是一种纯文本的数据记录,带有 5 个可变长度的字段:《
==Expires : cookie 的过期日期。如果此字段留空,cookie 会在访客退出浏览器时过期。
==Domain : 网站的域名。
==Path : 设置 cookie 的目录或网页的路径。如果您想从任意的目录或网页检索 cookie,此字段可以留空。
==Secure : 如果此字段包含单词 "secure",那么 cookie 只能通过安全服务器进行检索。如果此字段留空,则不存在该限制。
==Name=Value : cookie 以键值对的形式被设置和获取。
》
@16、设置 Cookies:向浏览器发送 cookies 是非常简单的。这些 cookies 会在 Content-type 字段之前,与 HTTP 头一起被发送。假设您想设置 UserID 和 Password 为 cookies,设置 cookies 的步骤如下所示:cout << "Set-Cookie:UserID=XYZ;\r\n"; cout << "Set-Cookie:Password=XYZ123;\r\n"; cout << "Content-type:text/html\r\n\r\n";
==注意:值得注意的是,cookies 是在发送行 "Content-type:text/html\r\n\r\n 之前被设置的。 编译上面的程序,生成 setcookies.cgi,并尝试使用下面的链接设置 cookies。它会在您的计算机上设置四个 cookies:/cgi-bin/setcookies.cgi
@17、获取 Cookies:检索所有设置的 cookies 是非常简单的。cookies 被存储在 CGI 环境变量 HTTP_COOKIE 中,且它们的形式如下:key1=value1;key2=value2;key3=value3....
实例:// 获取环境变量
const CgiEnvironment& env = cgi.getEnvironment();
for( cci = env.getCookieList().begin();
cci != env.getCookieList().end();
++cci )
{
cout << "<tr><td>" << cci->getName() << "</td><td>";
cout << cci->getValue();
cout << "</td></tr>\n";
}
@18、文件上传实例:为了上传一个文件,HTML 表单必须把 enctype 属性设置为 multipart/form-data。
==实例:
<form enctype="multipart/form-data"
action="/cgi-bin/cpp_uploadfile.cgi"
method="post">
<p>文件:<input type="file" name="userfile" /></p>
</form>
}
--STL 教程<标准模板库>
{
@1、STL教程:C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。
==C++ 标准模板库的核心包括以下三个组件:《
=容器(Containers) 容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。
=算法(Algorithms) 算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。
=迭代器(iterators) 迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。
》
}
--标准库{
@1、标准库:C++ 标准库可以分为两部分《
=标准函数库: 这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自 C 语言。
=面向对象类库: 这个库是类及其相关函数的集合。
》
}
@2、标准函数库:《
=输入/输出 I/O
=字符串和字符处理
=数学
=时间、日期和本地化
=动态分配
=其他
=宽字符函数
》
@3、面向对象类库:标准的 C++ 面向对象类库定义了大量支持一些常见操作的类,比如输入/输出 I/O、字符串处理、数值处理。面向对象类库包含以下内容:《
=标准的 C++ I/O 类
=String 类
=数值类
=STL 容器类
=STL 算法
=STL 函数对象
=STL 迭代器
=STL 分配器
=本地化库
=异常处理类
=杂项支持库
》
】