C++复习

C++基础

定义常量的两种简单方式:const和#define
C++的修饰符类型有signed, unsigned, long, short
C++程序中可用的存储类:auto、static、extern、mutable、thread_local
auto关键字用于两种情况:
1、声明变量时根据初始化表达式自动推断该变量的类型
2、声明函数时函数返回值的占位符

auto f = 3.14; //double
auto s("hello"); //const char*
auto z = new auto(9); //int*
auto x1 = 5, x2 = 5.0, x3 = 'r'; //错误,必须是初始化为同一类型

static存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用static修饰全局变量时,会使变量的作用域限制在声明它的文件内。
在C++中,当static用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。

extern存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用‘extern’时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用extern来得到已定义的变量或函数的引用。可以这么理解,extern是用来在另一个文件中声明一个全局变量或函数。

// main.cpp
#include 
int count;
extern void write_extern();
int main()
{
    count = 5;
    write_extern();
   return 0;
}
//support.cpp
#include 
extern int count;
void write_extern(void){std::cout<<"Count is"<

mutable存储类只能用于类的数据成员,不能用于普通变量。具有mutable性质的类的数据成员打破了类对象的const限定,允许修改类的mutable的数据成员,即便类的其他成员仍然是const只读属性。

thread_local存储类:使用thread_local说明符声明的变量尽可在它在其上创建的线程上访问。变量在创建线程时创建,并在销毁线程时销毁。每个线程都有其自己的变量副本。
thread_local说明符可以与static或extern合并。
可以将thread_local仅用于数据声明和定义,thread_local不能用于函数声明或定义。
C++运算符:算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、杂项运算符
位运算符:&、|、^、~、<<
杂项运算符:sizeof、Condition? X:Y、逗号、cast、
逗号运算符:使用逗号运算符是为了把几个表达式串在一起,整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。

var = (count=19, incr=10, count+1);

C++循环:while、for、范围for、do...while、嵌套循环
判断语句:if, if...else, 嵌套if, switch, 嵌套switch语句

#include 
using namespace std;
int main()
{
    char grade = 'D';
    switch(grade)
    {
        case 'A':
            cout << "很棒!"<

函数的引用调用:向函数传递参数的引用调用方法,把参数的地址复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。

#include 
void swap(int &x, int &y){int temp = x; x=y; y=temp;}
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表达式把函数看作对象。Lambda表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。

[](int x, int y){return xint {int z = x + y; return z + x;}
[] //没有定义任何变量。使用未定义变量会引发错误
[x, &y] //x以传值方式传入,y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显示地以引用方式加以引用。其余变量以传值方式加以引用。

C++随机数产生

#include 
#include 
#include 
using namespace std;
int main()
{
    int i,j; srand((unsigned)time(NULL));
    for(i=0;i<10;i++){j = rand(); cout<<"随机数:"<< j << endl;}
   return 0;
}

可以采用setw(13)函数来进行格式化输出。
C++引用:引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
C++引用vs指针:主要有三个不同,一个是不存在空引用。引用必须连接到一块合法的内存。二是一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向另一个对象。三是引用必须在创建时候被初始化。指针可以在任何时间被初始化。
typedef关键字:

typedef struct
{
    char title[50];
    char author[50];
    char subject[100];
    int book_id;
}Books;
typedef long int *pint32;
pint32 x, y, z;

C++面向对象

class Box
{
    public:
        double length;
        double breadth;
        double height;
};
Box Box1; Box Box2;

类的成员函数可以定义在类定义内部,或者单独使用范围解析运算符::来定义。在类定义中定义的成员函数把函数声明为内联的,即便没有使用inline标识符。

#include 
using namespace std;
class Box
{
    public:
        double length;
        double breadth;
        double height;
        double getVolume(void);
       void setLength(double len);
       void setBreadth(double bre);
       void set Height(double hei);
};
double Box::getVolume(void){ return length * breadth * height;}

public: 公有成员在程序中类的外部是可访问的。您可以不使用任何成员函数来设置和获取公有变量的值。
private: 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。默认情况下,类的所有成员都是私有的。
protected: 保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类中是可访问的。
类的构造函数是类的一种特殊成员函数,它会在每次创建类的新对象时执行。构造函数的名称与类的名称完全相同,并且不会返回任何类型,也不会返回void。构造函数可用于为某些成员变量设置初始值。

#include 
using namespace std;
class Line
{
    public:
        void setLength(double len);
        double getLength(void);
        Line();
    private:
        double length;
}
Line::Line(void){cout<<"Object is being created"<< endl;}

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序前释放资源。

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。如果在类中没有定义拷贝构造函数,编译器会自行定义一个。。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。

#include 
using namespace std;
class Line
{
    public:
        int getLength(void); Line(int len); Line(const Line &obj); ~Line();
    private:
        int *ptr;
};
Line::Line(int len){cout<<"Normal constructor allocating ptr"<

类的友元函数是定义在类外部,但有权访问类的所有私有成员和保护成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

class Box
{
    double width;
    public:
        double length;
        friend void printWidth(Box box);
       void setWidth(double wid);
};
friend class ClassTwo;

内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有代码,否则将会继续使用旧的函数。
如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字inline,在调用函数之前需要对函数进行定义。如果已定义的函数多于一行,编译器会忽略inline限定符。
在类定义中的定义的函数都是内联函数,即使没有使用inline说明符。

在C++中,每一个对象都能通过this指针来访问自己的地址。this指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。友元函数没有this指针,因为友元不是类的成员。只有成员函数才有this指针。

一个指向C++类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符->,就像访问指向结构的指针一样。与所有的指针一样,您必须在使用指针之前,对指针进行初始化。

我们可以使用static关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为0,我们不能把静态成员放置在类的定义中,但是可以在类的外部通过使用范围解析符::来重新声明静态变量从而对它进行初始化。

#include 
using namespace std;
class Box
{
    public:
    static int objectCount;
    Box(double l=2.0, double b=2.0, double h=2.0)
    {cout<<"Constructor called."<

如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符::就可以访问。
静态成员函数只能访问静态数据成员,不能访问其他静态成员函数和类外部的其他函数。
静态成员函数有一个类范围,他们不能访问类的this指针。您可以使用静态成员函数来判断类的某些对象是否已经被创建。

class Rectangle: public Shape
{
    public:
        int getArea(){return (width*height);}
}

派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为private。
一个派生类继承了所有的基类方法,除了基类的构造函数、析构函数和拷贝构造函数、基类的重载运算符、基类的友元函数。

公有继承:当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
保护继承:当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
私有继承:当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

多继承:一个子类可以有多个父类,它继承了多个父类的特性。
class <派生类名>: <继承方式1><基类名1>,<继承方式2><基类名2>,...

C++允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义不相同。
当调用一个重载函数或重载运算符时,编译器通过把所使用的参数类型与定义中的参数类型进行比较,决定选用最适合的定义。

重载的运算符是带有特殊名称的函数,函数名是由关键字operator和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
Box operator+(const Box&);
声明加法运算符用于把两个Box对象相加,返回最终的Box对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数。如Box operator+(const Box&, const Box&);

C++多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。

#include 
using namespace std;
class Shape{
  protected:
    int width,height;
  public:
    Shape(int a=0, int b=0){width=a; height=b;}
    int area(){cout<<"Parent class area:"<area();
    shape = &tri; shape->area(); return 0;
}

上述代码导致错误输出的原因是,调用函数area()被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接-函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为area()函数在程序编译期间就已经设置好了。

class Shape{
  protected:
    int width, height;
  public:
    Shape(int a=0, int b=0){width = a; height = b;}
    virtual int area(){cout<<"Parent class area:"<

此时编译器看的是指针的内容,而不是它的类型。因此,由于tri和rec类的对象的地址存储在*shape中,所以会调用各自的area()函数。
每个子类都有一个函数area()的独立实现。这就是多态的一般使用方式。有了多态,就可以有多个不同的子类,都带有同一个名称但是具有不同实现的函数,函数的参数甚至可以是相同的。

虚函数是在基类中使用关键字virtual声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。
我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。

纯虚函数:如果想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是在基类中又不能对虚函数给出有意义的实现,这个时候就会使用虚函数。

class Shape{
  protected:
    int width, height;
  public:
    Shape(int a=0, int b=0){width=a; height=b;}
    virtual int area() = 0;
};

C++数据抽象是指只向外界提供关键信息,并隐藏其后台的实现细节,只表现必要的信息而不呈现细节。数据抽象是一种依赖于接口和实现分离的编程技术。
在C++中,我们使用访问标签来定义类的抽象接口。一个类可以包含零个或多个访问标签:a.使用公共标签定义的成员都可以访问该程序的所有部分。一个类型的抽象数据视图是由它的公共成员来定义的。b.使用私有标签定义的成员无法访问到使用类的代码。私有部分对使用类型的代码隐藏了实现细节。
访问标签出现的频率没有限制。每个访问标签指定了紧随其后的成员定义的访问级别。指定的访问级别会一直有效,直到遇到下一个访问标签或者遇到类主体的关闭右括号为止。

数据封装是一种把数据和操作数据的函数捆绑在一起的概念,这样能避免受到外界的干扰和误用,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。

C++接口(抽象类):接口描述了类的行为和功能,而不需要完成类的特定实现。C++接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念。

如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用"=0"来指定的。

class Box
{
  public:
    virtual double getVolume()=0;
  private:
    double length; double breadth; double height;
};

设计抽象类(ABC)的目的,是为了给其他类提供一个可以继承的适当的基类。抽象类不能被用于实例化对象,它只能作为接口使用。如果试图实例化一个抽象类的对象,会导致编译错误。
因此,如果一个ABC的子类需要被实例化,则必须实现每个虚函数,这也意味着C++支持使用ABC声明接口。如果没有在派生类中重载纯虚函数,就尝试实例化该类的对象,会导致编译错误。
可用于实例化对象的类被称为具体类。

#include 
using namespace std;
class Shape
{
  public:
    virtual int getArea()=0;
    void setWidth(int w){width = w;}
    void setHeight(int h){height = h;}
  protected:
    int width; int height;
};

C++高级教程

回顾iostream标准库,它提供了cin和cout方法分别用于从标准输入读取流和向标准输出写入流。
C++的另一个标准库fstream则用于从文件读取流和向文件写入流。主要定义了三个新的数据类型:
ofstream 该数据类型表示输出文件流,用于创建文件并向文件写入信息
ifstream 该数据类型表示输入文件流,用于从文件读取信息
fstream 该数据类型通常表示文件流,且同时具有ofstream和ifstream两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。

open函数是fstream、ifstream和ofstream对象的一个成员。
void open(const char *filename, ios::openmode mode);
ios::app 追加模式。所有写入都追加到文件末尾
ios::ate 文件打开后定位到文件末尾
ios::in 打开文件用于读取
ios::out 打开文件用于写入
ios::trunc 如果该文件已经存在,其内容将在打开文件之前被截断,即把文件长度设置为0.
可以把两种或两种以上的模式结合使用。

ofstream outfile;
outfile.open("file.dat", ios::out|ios::trunc);
fstream afile;
afile.open("file.dat", ios::out|ios::in);

当C++程序终止时,它会自动关闭刷新所有流,释放所有分配的内存,并关闭所有打开的文件。
下面是close()函数的标准语法,close()函数是fstream、ifstream和ofstream对象的一个成员。

void close();

在C++编程中,我们使用流插入运算符<<向文件写入信息,就像使用该运算符输出信息到屏幕上一样。读取文件时亦类似。

#include 
#include 
using namespace std;
int main()
{
  char data[100];
  ofstream outfile;
  cout<<"Writing to the file"<> data; cin.ignore(); outfile<

istream和ostream都提供了用于重新定位文件位置指针的成员函数。这些成员函数包括关于istream的seekg和关于ostream的seekp。
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);

C++动态内存分为两个部分:栈是在函数内部声明的所有变量都将占用栈内存;堆是程序中未使用的内存,在程序运行时可用于动态分配内存。

double* pvalue = NULL; //初始化为null的指针
pvalue = new double; // 为变量请求内存
if(!(pvalue = new double)){cout<<"Error: out of memory."<
#include 
using namespace std;
namespace first_space{void func(){cout<<"Inside first_space"<

命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。

#include 
#include 
using namespace std;
template 
inline T const& Max(T const& a, T const& b){return a
#include 
#include 
#include 
#include 
#include 
using namespace std;
template 
class Stack{
private:
  vector elems;
public:
  void push(T const&); void pop(); T top() const;
  bool empty() const{return elems.empty();}
};
template 
void Stack::push(T const& elem){elems.push_back(elem);}
template
void Stack::pop()
{
if(elems.empty())
  throw out_of_range("Stack<>::pop():empty stack");
elems.pop_back();
}
template 
T Stack::top() const
{
  if(elems.empty()) throw out_of_range("Stack<>::top():empty stack");
  return elems.back();
}
int main()
{
  try{
  stack inStack;
  Stack stringStack;
  intStack.push(7); cout<
#define PI 3.14159
#define MIN(a,b) ((a)<(b)?a:b)
#define CONCAT(x,y) x##y
#define MKSTR( x ) #x
// C++多线程
#include 
pthread_create(thread, attr, start_outine, arg)
//thread 指向线程标识符指针
//attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值NULL
//start_routine 线程运行函数起始地址,一旦线程被创建就会执行
//arg 运行函数的参数。它必须通过把引用作为指针强制转换为void类型进行传递。如果没有传递参数,则使用NULL。
// 创建线程成功时,函数返回0,若返回值不为0则说明创建线程失败。
#include 
pthread_exit(status)
//该函数用于显式地退出一个线程。通常情况下,pthread_exit()函数是在线程完成工作后无需继续存在时被调用。

如果main()是在它所创建的线程之前结束,并通过pthread_exit()退出,那么其他线程将继续执行。否则,它们将在main()结束时自动被终止。

#include 
#include 
using namespace std;
#define NUM_THREADS 5
void* say_hello(void* args){cout<<"Hello Runoob!"<#include 
#include 
#include 
using namespace std;
#define NUM_THREADS
void *PrintHello(void *threadid)
{
  int tid = *((int*)threadid);
  cout<<"Hello Runoob! 线程ID, "<
#include 
#include 
#include 
using namespace std;
#define NUM_THREADS 5
struct thread_data{int thread_id; char *message;};
void *PrintHello(void *threadarg)
{
  struct thread_data *my_data;
  my_data = (struct thread_data *) threadarg;
  cout<<"Thread ID:"<thread_id;
  cout<<"Message:"<thread_id;
  pthread_exit(NULL);
}
int main()
{
  pthread_t threads[NUM_THREADS];
  struct thread_data td[NUM_THREADS];
  int rc; int i;
  for(i=0;i
pthread_join(threadid, status)
pthread_detach(threadid)

pthread_join()子程序阻碍调用程序,直到指定的threadid线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的或可分离的。只有创建时定义为可连接的线程才可以被连接。
如果线程创建时被定义为可分离的,则它永远也不能被连接。

C++资源库

C++ STL标准模版库是一套功能强大的C++模版类,提供了通用的模版类和函数,这些模版类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。
容器:容器是用来管理某一类对象的集合。C++提供了各种不同类型的容器,比如deque、list、vector、map等。
算法:算法作用于容器,它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。
迭代器:迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。
C++标准库可以分为两个部分:
标准函数库:这个库是由通用的、独立的、不属于任何类的函数组成的。函数库继承自C语言。
面向对象类库:这个库是类及其相关函数的集合。

你可能感兴趣的:(C++复习)