C++中的类

背景

C++中最重要的一个概念大概就是类和对象了,最近看了一些Skia的源码,就发现里面类的声明有很多中,自己在动手写的时候多少还是有些不知所措。所以就参考这个tutorial再学习一下C++里面类的语法,和每种写法的场景。

类的定义

首先,类是structure的一个延伸,struct主要是包含成员变量,class的话还可以包含成员函数。

另外,objectclass的区别在于,objectclass的一个实例,class是一个type,而object是一个变量。

类的使用语法就是使用class或者struct关键字:

class 类名 {
    public:
        // 公共的行为或属性
    protected:
        // 受保护的行为或属性
    private:
        // 私有的行为或属性
} 实例变量名;

其中实例变量名是一个可选的变量名列表。access specifiers是指里面的访问控制修饰符,是来控制成员访问的权限的:

  • private修饰的成员只能从相同类(或者friend)里的其他成员进行访问
  • protected修饰的成员可以从相同类(或者friend)里的其他成员之外,还可以从派生类(子类)中进行访问,也就是private访问权限之外加一个子类
  • public修饰的成员就可以从任何地方进行访问

在class关键字里面声明的所有成员默认都是private的。所以下面的写法相当于写width和height都是私有成员:

class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area (void);
} rect;

上面还有一个注意的是,Reactangle是类,rect是实例变量,这里只有声明,还没有到定义。这个关系就像下面的inta的关系,int是类名,a是变量名。

int a;

这里面私有和公有成员的访问权限就不细说了,应该大家都知道。成员函数的实现也可以通过::两个冒号在class之外进行定义,比如下面这样:

// classes example
#include 
using namespace std;

class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area() {return width*height;}
};

void Rectangle::set_values (int x, int y) {
  width = x;
  height = y;
}

int main () {
  Rectangle rect;
  rect.set_values (3,4);
  cout << "area: " << rect.area();
  return 0;
}

上面这个例子里面,area方法是直接在class里面定义的,而set_values方法是在class之外定义的。通过双冒号来区别于其他普通的非成员函数。在class内定义和class外定义成员方法的区别在于,在class类内定义的成员函数被编译器当作是inline成员函数,而外面的话只是一个普通的类成员函数,但这只是对编译器优化来说有区别,对使用没有区别。

构造函数

这个时候我们可以看到,如果成员函数areaset_values之前就被调用怎么办?就会返回一个未定义的成员变量widthheight。为了避免这种情况,类可以包含一个特殊的构造函数,当每次初始化时可以被自动的调用,初始化成员变量和内存的分配。
构造函数被声明就像一个普通的成员函数,但是名字和类名一样,并且没有返回类型,void都没有。它只会在生成实例的时候被执行一次,且没有返回值!!只是一个简单的对象初始化。

构造函数重载

和其他的函数一样,构造函数也可以通过不同的参数(不同的参数个数或者参数类型)来进行重载,编译器会自动的调用匹配参数的函数。比如下面这个例子中:

// overloading class constructors
#include 
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle ();
    Rectangle (int,int);
    int area (void) {return (width*height);}
};

Rectangle::Rectangle () {
  width = 5;
  height = 5;
}

Rectangle::Rectangle (int a, int b) {
  width = a;
  height = b;
}

int main () {
  Rectangle rect (3,4); // 12
  Rectangle rectb; // 25
  cout << "rect area: " << rect.area() << endl;
  cout << "rectb area: " << rectb.area() << endl;
  return 0;
}

这个里面下面2个都是调用构造函数的:

Rectangle rectc(3,4);
Rectangle rectb;

但是也有差别,rectb的构造是调用了默认的构造函数,而rect调用的是接受a,b入参的重载的构造函数。这里面还要注意的是默认构造函数不能用下面的方式来调用:

Rectangle rectc();

加上括号之后就不能调用默认构造函数,而是要调用一个不接受入参的重载构造函数了。

统一的初始化

像上面那样调用构造函数的方法是把入参传入括号中的初始化方式,是函数式的形式(functional form)。但是构造函数也可以用其他的语法来调用。只有一个入参的构造函数可以用下面的方式:

class_name object_name = initialization_value;

最近的话C++又有了uniform initialization的初始化语法,和上面函数式的形式一样,可以用大括号{}替代中括号()来包裹参数。

// classes and uniform initialization
#include 
using namespace std;

class Circle {
    double radius;
  public:
    Circle(double r) { radius = r; }
    double circum() {return 2*radius*3.14159265;}
};

int main () {
  Circle foo (10.0);   // functional form
  Circle bar = 20.0;   // assignment init.
  Circle baz {30.0};   // uniform init.
  Circle qux = {40.0}; // POD-like

  cout << "foo's circumference: " << foo.circum() << '\n';
  return 0;
}

在之前的例子的话就是

Rectangle rectb;   // default constructor called
Rectangle rectc(); // function declaration (default constructor NOT called)
Rectangle rectd{}; // default constructor called 

这个形式的选择只是风格的问题,没有其他的含义。

构造函数中的成员初始化

成员变量的初始化可以不用函数体就直接完成,语法就是在构造函数之前用一个冒号(:)和一个初始化的列表来声明,比如下面这个构造函数Rectangle:

class Rectangle {
    int width,height;
  public:
    Rectangle(int,int);
    int area() {return width*height;}
};

可以用下面几种方式来写:

Rectangle::Rectangle (int x, int y) { width=x; height=y; }
Rectangle::Rectangle (int x, int y) : width(x) { height=y; }
Rectangle::Rectangle (int x, int y) : width(x), height(y) { }

这上面第一种是正常的用一个函数体来写,后面2种是width(x), { height=y; }, height(y){} 。

如果是基础类型的成员变量,上面的方式就可以进行初始化,但是如果成员变量是一个对象的时候,当没有被初始化赋值时,就会调用这个对象的默认构造函数。

类的指针

对象也可以通过指针来指向,一旦被神明来,这个类就成为了一个有效的type,可以用这个类型的指针来使用,比如:

Rectangle * prect;

是一个指向类Rectangle的指针。和结构struct里面一样,对象的成员的调用是用 .操作符,而指针是用->操作符。

// pointer to classes example
#include 
using namespace std;

class Rectangle {
  int width, height;
public:
  Rectangle(int x, int y) : width(x), height(y) {}
  int area(void) { return width * height; }
};


int main() {
  Rectangle obj (3, 4);
  Rectangle * foo, * bar, * baz;
  foo = &obj;
  bar = new Rectangle (5, 6);
  baz = new Rectangle[2] { {2,5}, {3,6} };
  cout << "obj's area: " << obj.area() << '\n';
  cout << "*foo's area: " << foo->area() << '\n';
  cout << "*bar's area: " << bar->area() << '\n';
  cout << "baz[0]'s area:" << baz[0].area() << '\n';
  cout << "baz[1]'s area:" << baz[1].area() << '\n';       
  delete bar;
  delete[] baz;
  return 0;
}

操作符的使用语法可以参考下面的列表:
C++中的类_第1张图片

你可能感兴趣的:(c++,程序员)