C++ tutorial for C users

2014年09月13日 10:11GMT+8

原文: http://www.4p8.com/eric.brasseur/cppcen.html, 默认使用 gcc 的 g++ 命令编译下边示例, 如果使用 gcc 则需加上 -lstdc++

一些编译器的问题:

  • 对于 cl.exe(msvc) 如果有中文,源代码一定不能以 utf-8(无BOM)保存, 否则将乱码, 参考…

头文件引用

C++ 的 #include 在引用头文件时, 不需要添加头文件扩展名 .h, 当 引用标准 C 库时,需要以字母 c 开头. C++ 的一些标准库需要添加命名空间 using namespace std

using namespace std;
#include <iostream>        // This is a key C++ library
#include <cmath>           // c++ 引用标准 C 库
// #include <math.h>       // c 语言形式则是以 .h 结尾

int main (){
   double a;
   a = 1.2;
   a = sin (a);
   cout << a << endl;
   return 0;
}

输入和输出

从键盘输入和输出到屏幕可以使用 流操作 ` cout «cin »`:

using namespace std;
#include <iostream>

int main()
{
   char s [100];             // s points to a string of max 99 characters

   cout << "This is a sample program." << endl;

   cout << endl;             // Just a line feed (end of line)

   cout << "Type your name: ";
   cin >> s;

   cout << "Hello " << s << endl;
   cout << endl << endl << "Bye!" << endl;

   return 0;
}

同样可以对文件流进行操作.

using namespace std;
#include <iostream>
#include <fstream>

int main (){
   fstream f;

   f.open("test.txt", ios::out);

   f << "This is a text output to a file." << endl;

   double a = 345;

   f  << "A number: " << a << endl;

   f.close();

   return 0;
}

从文件中读取:

using namespace std;
#include <iostream>
#include <fstream>

int main (){
   fstream f;
   char c;
   cout << "What's inside the test.txt file" << endl;
   cout << endl;
   f.open("test.txt", ios::in);
   while (! f.eof() ){
      f.get(c);                          // Or c = f.get()
      cout << c;
   }
   f.close();
   return 0;
}

cout 有 width(n)和 setw() 二个方法用于格式化输出. 这二个方法仅影响到下一次输出, 注意: 只一次.

using namespace std;
#include <iostream>
#include <iomanip>

int main (){
   int i;
   cout << "A list of numbers:" << endl;
   for (i = 1; i <= 1024; i *= 2){
      cout.width (7);
      cout << i << endl;
   }
   cout << "A table of numbers:" << endl;
   for (i = 0; i <= 4; i++){
      cout << setw(3) << i << setw(5) << i * i * i << endl; 	// setw 有点像 \t
   }
   return 0;
}

变量声明及作用域

变量可以在代码的任意位置声明,大括号或 for 循环中都属于局部变量. 其实目前 C 是一样的.

引用全局同名变量

using namespace std;
#include <iostream>
double a = 128;
int main (){
   double a = 256;
   cout << "Local a:  " << a   << endl;
   cout << "Global a: " << ::a << endl; // 注意 双冒号 的用法
   return 0;
}

引用

引用必须在声明时就确定其引用的变量, 因此引用不可变。

using namespace std;
#include <iostream>

int main (){
   double a = 3.1415927;
   double &b = a;    // <--
   b = 89;
   cout << "a contains: " << a << endl;
   return 0;
}

引用可以用来函数参数以修改传送的参数值,有一个缺点就是调用带引用参数的函数时形为不明确,不像 C# 那样传递参数为引用时必须加 ref(或者我们可以定义一个空)

void change (double &r, double s){
   r = 100;
   s = 200;
}

int main (){
   double k, m;
   k = 3;
   m = 4;
   change (k, m);
   cout << k << ", " << m << endl;  // Displays 100, 4.
   return 0;
}

如果你使用指针, 看起来应该像这样.

using namespace std;
#include <iostream>

void change (double *r, double s){
   *r = 100;
   s = 200;
}

int main (){
   double k, m;
   k = 3;
   m = 4;

   change (&k, m);

   cout << k << ", " << m << endl;        // Displays 100, 4.
   return 0;
}

对于从函数中返回引用, 实际上根本没必要这样做, 因为你不能返回局部引用,只能返回引用参数,这样做就没有意义了

double &biggest (double &r, double &s){
   if (r > s) return r;
   else       return s;
}

命名空间

使用 :: 访问各命名空间下, 例如: std::cout << "Hello" << std::endl

using namespace std;
#include <iostream>
#include <cmath>
namespace first{
   int a;
   int b;
}
namespace second{
   double a;
   double b;
}
int main (){
   first::a = 2;
   first::b = 5;
   second::a = 6.453;
   second::b = 4.1e4;
   cout << first::a + second::a << endl;
   cout << first::b + second::b << endl;
   return 0;
}

内联替换

inline 内联替换,常用于对性能有要求的小代码块,或常在宏替换中出现.

using namespace std;
#include <iostream>
#include <cmath>
inline double hypothenuse (double a, double b){
   return sqrt (a * a + b * b);
}
int main (){
   double k = 6, m = 9;
   cout << hypothenuse (k, m) << endl;
   cout << sqrt (k * k + m * m) << endl;
   return 0;
}

捕获异常

try,catch,throw

using namespace std;
#include <iostream>
#include <cmath>
int main (){
   int a, b;
   cout << "Type a number: ";
   cin >> a;
   cout << endl;
   try{
      if (a > 100) throw 100;
      if (a < 10)  throw 10;
      throw a / 3;
   } catch (int result){ // 根据 throw 会抛出什么类型的异常
      cout << "Result is: " << result << endl;
      b = result + 1;
   } catch (const char * message){

   }
   cout << "b contains: " << b << endl;
   return 0;
}

默认参数

可以为函数定义默认参数.

using namespace std;
#include <iostream>
double test (double a, double b = 7){
   return a - b;
}

int main (){
   cout << test (14, 5) << endl;    // Displays 14 - 5
   cout << test (14) << endl;       // Displays 14 - 7
   return 0;
}

如果 要将函数声明写到 头文件中去, 则把默认参数写在声明上,在函数定义那里就不要写了

// 函数声明
double test (double, double = 7)

// 函数定义
double test (double a, double b){
	return a - b;
}

函数重载

C++ 的一个重要特性. 对于同名函数或方法,只要参数或返回值不一致, 将会自动重载. 其实就是编译器帮你改名了而已.

using namespace std;
#include <iostream>

double test (double a, double b){
   return a + b;
}
int test (int a, int b){
   return a - b;
}
int main (){
   double   m = 7,  n = 4;
   int      k = 5,  p = 3;

   cout << test(m, n) << " , " << test(k, p) << endl;

   return 0;
}

操作符重载

OPERATOR OVERLOADING, 注意: 操作符重载使用代码理解难度加大, http://www.4p8.com/eric.brasseur/cppcen.html#l8

using namespace std;
#include <iostream>

struct vector{
   double x;
   double y;
};

vector operator * (double a, vector b){
   vector r;
   r.x = a * b.x;
   r.y = a * b.y;
   return r;
}

// 重载 << 返回新的数据类型.
ostream& operator << (ostream& o, vector a){
   o << "(" << a.x << ", " << a.y << ")";
   return o;
}

int main (){
   vector k, m;              // No need to type "struct vector"

   k.x =  2;                 // To be able to write
   k.y = -1;                 // k = vector (2, -1)

   m = 3.1415927 * k;        // 重载 *

   cout << "(" << m.x << ", " << m.y << ")" << endl;

   count << m << endl;	     // 重载 <<
   return 0;
}

模板

对于 C++ 的函数重载, 你当然不会想要为每个类型都写一个同名方法, 不知道是否应该叫做 泛型,或者 C++ 依靠模板实现泛型,

template <class T>
T minimum (T a, T b){
   T r;
   r = a;
   if (b < a) r = b;
   return r;
}

int main (){
   int i1, i2, i3;
   i1 = 34;
   i2 = 6;
   i3 = minimum (i1, i2);
   cout << "Most little: " << i3 << endl;
   double d1, d2, d3;
   d1 = 7.9;
   d2 = 32.1;
   d3 = minimum (d1, d2);
   cout << "Most little: " << d3 << endl;
   return 0;
}

多类型, 不过这种方式很混乱的..

template <class T1, class T2>
T1 minimum (T1 a, T2 b){
   T1 r, b_converted;
   r = a;
   b_converted = (T1) b;
   if (b_converted < a) r = b_converted;
   return r;
}

类模板: 原文 http://blog.csdn.net/richerg85/article/details/7565870

//类模板,模板定义中class和typename是没有什么区别的
//模板的声明和定义只能在全局、命名空间或者类范围内进行。
template<class T1,class T2>
class A
{
public:
    void f(T1 a, T2 b);
};
template<class T1,class T2> void A<T1,T2>::f(T1 a,T2 b)
{
    cout << "class A------>T1:" << a <<";T2:" << b << endl;
}
//定义类模板的默认类型形参,默认类型形参不适用于函数模板。
template<typename T3, typename T4=int>//T4是默认模板类型形参
class B
{
private:
    T3 t3;
    T4 t4;
public:
    B(T3 a, T4 b);
    void show();
};
template<class T3,class T4> B<T3,T4>::B(T3 a, T4 b):t3(a),t4(b){}
//template<class T3,class T4=int> B<T3,T4>::B(T3 a, T4 b):t3(a),t4(b){},这样是错误的,
//在类模板外部定义带有默认类型的形参时,在template的形参表中默认值应该省略
template<class T3,class T4> void B<T3,T4>::show()
{
    cout << "class B------>T3:" << t3 <<";T4:" << t4 << endl;
}

//非类型模板参数。
//非类型形参只能是整型、指针和引用,像double,string,string **这样的类型是不允许的,但是double &,double *对象的引用或指针是正确的。
template<class T5,int a>
class C
{
private:
    T5 max[a];
public:
    void cshow()
    {
        cout << "class C------>T5:" << typeid(T5).name()<< endl;
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    //基本模板类测试
    A<int,int> a1;
    a1.f(2,3);
    A<int,char> a2;
    a2.f(2,'a');
    A<string,int> a3;
    a3.f("hello word!",5);

    //带有默认类型形参的模板类
    B<char,char> b1('a','b');
    b1.show();
    B<string,string> b2("你好","测试中......");
    b2.show();
    B<int,char> b3(25,'F');
    b3.show();

    //非类型模板参数
    const int i = 5;
    C<int,i> c1;
    c1.cshow();
    //int j = 5;
    //C<int,j> c2; //错误,调用非类型模板形参的实参必须是常量表达式
    C<char,i> c2;
    c2.cshow();
    return 0;
}

new delete

C++ 新的关键字. new 将返回一个对应的类型指针, 而 delete 将回收指针指向的内存. 像是 C 中的 malloc 和 free。 但由于是关键字的原因,因此编译器能优化 new 和 delete.

using namespace std;
#include <iostream>
#include <cstring>

int main (){
   double *d;
   d = new double;
   *d = 45.3;
   cout << "Type a number: ";
   cin >> *d;
   *d = *d + 5;
   cout << "Result: " << *d << endl;
   delete d;
   d = new double[15];   // 相当于 d = (double *) malloc(sizeof(double) * 15);
   d[0] = 4456;
   d[1] = d[0] + 567;
   cout << "Content of d[1]: " << d[1] << endl;
   delete [] d; 		// 相当于 free(d);
   int n = 30;
   d = new double[n];                 // new can be used to allocate an
                                      // array of random size.
   for (int i = 0; i < n; i++)   {
      d[i] = i;
   }
   delete [] d;
   char *s;
   s = new char[100];
   strcpy (s, "Hello!");
   cout << s << endl;
   delete [] s;
   return 0;
}

对于new double[n]之后,对于 delete 是否需要加上括号 []:

《深度探索C++对象模型》P259的描述,“寻找数组维度给delete运算符的效率带来极大的影响,所以才导致这样的妥协:

只有在中括号出现时,编译器才寻找数组的维度,否则它便假设只有单独一个objects要被删除。”

  • delete [] 释放空间,并且调用了每个对象的析构函数

  • delete 释放空间, 并且调用了第一个的析构函数

  • 对于 char,int,float,double,struct ,基本类型的对象没有析构函数,他们等价

结构体

在标准 C中, struct 只能包含数据(定义函数指针也是蛮麻烦的).在 c++ 中, 可以包含 函数. 在 C++ 中, struct 其实和 class 区别不大.

using namespace std;
#include <iostream>

struct vector{
   double x;
   double y;
   double surface (){
      double s;
      s = x * y;
      if (s < 0) s = -s;
      return s;
   }
};

int main (){
   vector a = {x:5,y:6};		// vs 2013 初使化报错,  vector a = {5, 6}; 通过
   cout << "The surface of a: " << a.surface() << endl;
   a.x = 7;
   cout << "The surface of a: " << a.surface() << endl;
   return 0;
}

  • 如果定义了 构造函数或继承了虚函数的话,就不能用 大括号 进行初使化

  • class 默认成员访问为 private, struct 默认为 public.

  • class 继承默认是 private,而struct继承默认是public.

  • 对于 template <struct T>, 将报错为 未定义的类型, 因此需要写成 template <class T>

using namespace std;
#include <iostream>

class vector{
public:
   double x;
   double y;

   double surface ()
   {
      double s;
      s = x * y;
      if (s < 0) s = -s;
      return s;
   }
};


int main (){
   vector a;
   a.x = 3;
   a.y = 4;
   cout << "The surface of a: " << a.surface() << endl;
   return 0;
}

构造函数和析构函数:

注意构造函数不允许有返回值.

class person{
public:
   char *name;
   int age;

   person (const char *n = "no name", int a = 0)
      name = new char [100];                 // better than malloc!
      strcpy (name, n);
      age = a;
      cout << "Instance initialized, 100 bytes allocated" << endl;
   }

   ~person (){
      delete name;          // instead of free!
                            // delete [] name would be more
                            // academic but it is not vital
                            // here since the array contains
                            // no C++ sub-objects that need
                            // to be deleted.
      cout << "Instance going to be deleted, 100 bytes freed" << endl;
   }
};

int main (){
    cout << "Hello!" << endl << endl;
    person a;
    cout << a.name << ", age " << a.age << endl << endl;

    person b ("John");
    cout << b.name << ", age " << b.age << endl << endl;

    b.age = 21;
    cout << b.name << ", age " << b.age << endl << endl;

    person c ("Miki", 45);
    cout << c.name << ", age " << c.age << endl << endl;

    cout << "Bye!" << endl << endl;
   return 0;
}

一个简单的 array 的定义. 通过重载 [], 实现一个防止越界的数组

class array{
public:
   int size;
   double *data;

   array (int s){
      size = s;
      data = new double [s];
   }

   ~array (){
      delete [] data;
   }

   double &operator [] (int i){
      if (i < 0 || i >= size){
         cerr << endl << "Out of bounds" << endl;
         exit (EXIT_FAILURE);
      }
      else return data [i];
   }
};

int main (){
   array t (5);

   t[0] = 45;                       // OK
   t[4] = t[0] + 6;                 // OK
   cout << t[4] << endl;            // OK
   t[10] = 7;                       // error!
   return 0;
}

值复制和复制引用

主要是使用 操作符重截

class person
{
public:
   char *name;
   int age;
   person (char *n = "no name", int a = 0){
      name = new char[100];
      strcpy (name, n);
      age = a;
   }
   person (const person &s){              // The COPY CONSTRUCTOR
      name = new char[100];
      strcpy (name, s.name);
      age = s.age;
   }
   person& operator= (const person &s){   // overload of =
      strcpy (name, s.name);
      age = s.age;
      return *this;
   }
   ~person () {
      delete [] name;
   }
};

// 仅传送引用
void modify_person (person& h){
   h.age += 7;
}

// 当传送参数为 h时, 将调用上边的 COPY CONSTRUCTOR, 这点需要理解
// 返回值会因为 赋值操作符的重载,而返回引用
person compute_person (person h){
   h.age += 7;
   return h;
}

int main (){
   person p;
   cout << p.name << ", age " << p.age << endl << endl; // output: no name, age 0

   person k ("John", 56);
   cout << k.name << ", age " << k.age << endl << endl; // output: John, age 56

   p = k;						// 重载, 引用赋值
   cout << p.name << ", age " << p.age << endl << endl; // output: John, age 56

   p = person ("Bob", 10);
   cout << p.name << ", age " << p.age << endl << endl; // output: Bob, age 10


   modify_person (p);			// 只是引用,不是构造函数复制也不是赋值重载

   cout << p.name << ", age " << p.age << endl << endl; // output: Bob, age 17

   // COPY CONSTRUCTOR 将被调用,然后产生一个新的 person 对象,
   // 函数体中修改其值并返回, 由于 赋值 = 已经重载, 因此返回的是引用
   k = compute_person (p);

   cout << p.name << ", age " << p.age << endl << endl; // output: Bob, age 17

   cout << k.name << ", age " << k.age << endl << endl; // output: Bob, age 24
   return 0;
}

COPY CONSTRUCTOR 允许你的程序创建基于实例的副本,为 key method.

上边所有示例中, 这些方法定义于 class 的声明处(即定义在头文件处),将使它们自动转为 inline.

The copy constructor allows your program to make copies of instances when doing calculations. It is a key method.

In all the examples above, the methods are defined inside the class definition. That automatically makes them inline methods

方法体位置

如果一个方法不可以为 inline,或你不想成为 inline, 或你想保持类定义的最小信息,你仅需要把方法体放在类声明的外部.(或分开放置于.h及.cpp)

class vector
{
public:
   double x;
   double y;
   double surface();         // 类声明中声明方法,但是不包含方法体,需要以 ; 结尾
};

double vector::surface()	 // 方法体放置于类声明外部,避免 inline
{
   double s = 0;

   for (double i = 0; i < x; i++)
   {
      s = s + y;
   }

   return s;
}

头文件和源文件

大型项目中因为常常要把源码编译成 .lib|.a|.so| 库文件, 所以需要写成 .h 和 .cpp的形式,

vector.h

class vector{
public:
   double x;
   double y;

   double surface();
};

vector.cpp

using namespace std;
#include "vector.h"

double vector::surface(){
   double s = 0;

   for (double i = 0; i < x; i++){
      s = s + y;
   }
   return s;
}

静态字段

C++ 中 静态成员变量不能在类声明处初始化,只能在外部初使化.在初始化值时也必须加上类型. 通过 className::fieldName 双冒号形式访问静态字段.

但被限定为 const 的变量可以在声明时初始化.

class Vector{
public:
   double x;
   double y;
   static int count;
   static const double PI = 3.1415926;

   Vector (double a = 0, double b = 0){
      this->x = a;
      this->y = b;
      count++;
   }
   ~Vector(){
      count--;
   }
};
int Vector::count = 0; // 外部初使化, 并且加上类型, 这里是 int

派生和继承

DERIVED and INHERITS,

using namespace std;

#include <iostream>
#include <stdio.h>
#include <math.h>

class Vector{
public:
	double x;
	double y;

	Vector (double a = 0, double b = 0){
		x = a;
		y = b;
	}

	double module(){
   		return sqrt(x*x + y*y);
	}

	double surface(){
		return x * y;
	}

	double sum(){
		return x + y;
	}
};

class TriVector: public Vector{     // TriVector 从 Vector 派生而来, 使用冒号 :.
public:
	double z;	// 增加成员属性

	TriVector(double a = 0, double b = 0, double c = 0):Vector(a,b){
		z = c;
                                    // Vector 构造函数将在 TriVector 构造函数之前被调用, 类似于 super(a,b)
                                    // 同样使用冒号 :.
	}

	TriVector(Vector& a){			// 当出现赋值 trivector = vector 时.将自动调用这个方法. 值复制
		x = a.x;
		y = a.y;
		z = 0;
	}

	double module(){                // 重定义 module 方法, 不需要写 override 关键字
		return sqrt(x*x + y*y + z*z);
	}

	double sum(){
		return Vector::sum() + z;   // override, 由于 C++ 允许多继承, 所以没有 super.method .
	}

	double volume(){
		return this->surface() * z;
	}
};


int main(int argc, const char ** argv){
	Vector v(4,5);
	TriVector t(1,2,3);

	cout << "Surface of v: " << v.surface() << endl;
	cout << "Volume of t: " << t.volume() << endl;

	TriVector t2;
	t2 = v;	     // 值复制, 自动调用 TriVector(Vector& a) 方法, 做一些值修改

	Vector v2;
	v2 = t;      // 值复制, z 值将会被自动丢弃
                 // 编译器自动调用类似于 Vector(TriVector& t),的方法, 不用自已实现

	cout << "Surface of t2: " << t2.surface() << endl;
	cout << "Surface of v2: " << v2.surface() << endl;
	return 0;
}

override 只能用在基类带有 virtual 的方法上。

class Base 
virtual void f();
}

class Derived : public Base {
void f() override;  // 表示派生类重写基类虚函数f
void F() override;  //错误:函数F没有重写基类任何虚函数
}

虚函数

如下示例, 当一个 Vector 的指针指向 TriVector实例时, 当调用 module()时,指向的将是 Vector 的方法

int main(int argc, const char ** argv){
	TriVector t(1,2,3);

	Vector *r = &t; // Vector指针, 但是指向 TriVector 的实例,

	cout << "module of r: " << r->module() << endl;		// output: 2.23607
	cout << "module of t: " << t.module() << endl;		// output: 3.74166
	return 0;
}

因此, 在上边示例中,如果 Vector 类的 module 加上 virtual 关键字,同样的示例 那么结果就不一样了:

	virtual double module(){
   		return sqrt(x*x + y*y);
	}

// ......上边同样的示例,

	TriVector t(1,2,3);
	Vector *r =  &t; // Vector指针, 但是指向 TriVector 的实例,

	cout << "module of r: " << r->module() << endl;  // output: 3.74166
	cout << "module of t: " << t.module() << endl;   // output: 3.74166

结论: C++ 通过 virtual 来实现类似于 其它语言称为 Interface 的东西.

using namespace std;
#include <iostream>
#include <math.h>

class Octopus{
public:
	virtual double module() = 0;  // = 0 强制方法未定义, 这可以使这个类无法被声明.
};

class Vector: public Octopus{
public:
	double x;
	double y;
	Vector (double a = 0, double b = 0){
		x = a;
		y = b;
	}
	double module(){
   		return sqrt(x*x + y*y);
	}
};


class Number: public Octopus{
public:
	double n;
	Number(double a = 0){
		n = a;
	}
	double module(){
   		return n>=0 ? n : -n;
	}
};


double biggest_module(Octopus *a){
	return a->module();
}


int main(int argc, const char ** argv){
	Vector v1(1,2), v2(6,7), v3(100,10);
	Number n1(5), n2(-3), n3(-150);

	cout << biggest_module(&v1) << " - " << biggest_module(&v2) << " - " << biggest_module(&v3) << endl;
	cout << biggest_module(&n1) << " - " << biggest_module(&n2) << " - " << biggest_module(&n3) << endl;
	return 0;
}

访问控制

  • public: 公共

  • protected: 保护, 继承类可以访问基类的 protected 成员.

  • private: 私有

访问继承, 例如: class Vector: public Octopus{}

基类 public protected private
public 继承 public protected 不可见
protected 继承 protected protected 不可见
private 继承 private private 不可见

多重继承

用逗号 , 分隔就好了. 大多数语言只能单个继承(因为它们能实现多个接口).

// 省略基类

class TriVector: public Vector, public Number{
	TriVector(double a, double b, double c): Vector(a,b),Number(c){

	}
}

其它

和大多数语言(es-X, c#, java)不一样的是 c++ 的变量传递 如果没有显示地声明为”引用”则全部按值拷贝进行传递

  • 指针快速索引

    int const *n;    // 指针可变,指向的值不可变
    
    int *const m;    // 指针不可变,指向的值可变
    
    const int const *mn;
    
    int *a[];        // array of pointers. 英文的意思更清楚
    
    int (*a)[];      // pointer to array.
    
    int *f();        // 返回一个int类型指针, 这种风格应该尽量避免,不如传指针为参数
    
    int (*f)();      // 函数指针.
    
    // 多唯数组指针对应
    int x[10][20];
    int(*px)[20];
    px = x;			//等同于 px = &x[0]
    
  • 结构体的初使化

    struct Person{
      int id;
      int phone;
    };
    // 以下各编译器兼容
    struct Person p1 = {10, 101};
    struct Person p2 = {.id = 10, .phone = 101};
    
  • #ifdef __cplusplus 一些源码能常见到的.

    C++ 语言在编译的时候为了解决函数的多态问题,会改变函数名称,但 C 语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器, 请保持我的名称,不要给我生成用于链接的中间函数名.

    #ifdef __cplusplus
    	extern "C" { // extern C 修饰变量和函数按照 C 语言方式编译和连接;
    #endif
    
    	void gme_clear_playlist( Music_Emu* );
    
    #ifdef __cplusplus
    	}	// extern C 结尾
    #endif
    
  • 方法后边接…

    // 接 const 表示这个函数不会修改成员变量.
    int current_track() const {
    
    // 接 throw() 表示这个函数不允许抛出异常, (c++11 还有个形为一样的 noexcept 但 msvc 不支持)
    const char* what() const throw() {  // c++98
    
    // 接 override, 表示覆盖父类的同名虚函数, (个人注: override 应该放到上边几个的最后)
    const char* what() const throw() override {
    
    // 虚函数置 0 为纯虚函数。 这使得这个类不允许直接初使化, 只能通过子类初使化.
    virtual void test() = 0;
    
  • 初使化成员列表, 可以初使化 const 类型成员.

    如果有一个类成员, 类型为 类或结构, 而这个成员需要参数来初使化, 这时就需要对这个类成员进行初使化.

    class Vector{
    public:
    	double x;
    	double y;
    	const double PI;
    	Vector(): x(1.0), y(1.0), PI(3.1415926){
    
    	}
    };
    
  • 匿名 namespace

    相对于 C 的 static 声明来说, 可以在匿名的空间里面声明很多变量和函数,这样可以省去了对每个变量和函数添加static声明.

    实质上匿名空间的功能跟static声明是一样的

  • define 中的 ###

    # 在宏展开时会将 # 后边的参数替换成字符串

    #define p(exp) printf(#exp)
    // 调用 p(test) 展开后为: printf("test")
    

    ## 将前后两个的单词拼接在一起。

    #define cat(x,y) x##y
    // 调用 cat(var, 123) 展开后为: var123
    

    #@ 将值序列变为一个字符

    #define ch(c) #@c
    // 调用 ch(a) 展开后为: 'a'
    
  • 关键字扩展

    // 示例:
    #ifdef _MSC_VER
      #if defined(HXCPP_DLL_IMPORT)
         #define HXCPP_EXTERN_CLASS_ATTRIBUTES __declspec(dllimport)
      #else
         #define HXCPP_EXTERN_CLASS_ATTRIBUTES __declspec(dllexport)
      #endif
    #else
      #if defined(HXCPP_DLL_EXPORT)
         #define HXCPP_EXTERN_CLASS_ATTRIBUTES __attribute__((visibility("default")))
      #else
         #define HXCPP_EXTERN_CLASS_ATTRIBUTES
      #endif
    #endif
    
  • explicit 用来修饰类的构造函数,防止隐式转换 http://www.educity.cn/develop/461209.html

  • 三条规则, 第一条(copy constructor)可以加 explicit 明确地禁止隐式赋值,这样将导致赋值不成立

    注: 由于 move 的引用, 因此下列 第 一, 二项可以多加上一个 & 就成了 move 相关的.

    // 1. copy constructor, 这个是使用另一个 Person 初使化时调用
    Person(const Person& that) : name(that.name), age(that.age){
    }
    
    // 2. copy assignment operator,这个是初使化后赋值时调用
    Person& operator=(const Person& that){
    	name = that.name;
    	age = that.age;
    	return *this;    // 注: assignment 必须要有返回才可以.
    }
    
    // 3. destructor
    ~Person(){
    }
    
    // ...
    Person p1;
    
    Person p2 = p1; // 1. copy constructor
    
    p2 = p1;        // 2. copy assignment operator
    
  • 子类中使用 using 声明引入基类成员 http://www.cnblogs.com/ustc11wj/archive/2012/08/11/2637316.html

  • operator new http://blog.sina.com.cn/s/blog_3c6889fe0100tqe8.html