C++ 总结 (二、面向对象)

C++ 总结 (二、面向对象)

官网

本文总结面向对象部分。

Class 类

是由结构体扩展而来,内部成员包含 实例变量函数
对象 是类实例,类是它的类型,对象是变量。
类成员(实例变量和函数)有三种访问权限: privateprotectedpublic

  • private - 仅仅类内部可以访问(类成员默认访问权限)
  • protected - 仅仅类内部和其子类可以访问
  • public - 任何对象可见的地方都可以访问

简单来说,定义和声明一个类与变量 class Rectangle rect 格式如下:

class Rectangle {
    int width, height;  // 默认private 访问权限
  public:
    void set_values (int,int);
    int area (void);
} rect;

如上,类中的方法可以只声明,具体实现放到外部,外部写实现可使用域操作符实现。完整实现如下:

// classes example
#include <iostream>
using namespace std;

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

// 在外部实现类中的 set_values 函数
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 12

构造器(类初始化器)

想一下,上面例子中如果没有调用 rect.set_values (3,4);直接获取 area 会怎样?

构造器是类进行初始化的一个函数。它只会在初始化类对象时调用一次,它没有返回值,仅仅是用来初始化对象。构造器也是类的一个成员函数,它会在创建对象的时候自动调用,无法被显示调用。构造器函数名称同类名一样,且无任何返回值(即使是void)

// example: class constructor
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle (int,int); // 构造器函数
    int area () {return (width*height);}
};

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

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

// 注意,此例中么有 set_values 函数,但是有一个相同效果的构造器函数。

构造器重载

构造器函数和普通函数一样可以构成重载(参数数量不同/参数类型不同)。

默认构造器: 与类名同名函数, 无参数,无返回值。其特殊在于: 当类对象声明的时会自动调用默认初始化器

// 对象声明就会调用默认初始化器
Rectangle rectb;   // ok, default constructor called
// 加括号是声明一个无参函数的语法(返回值是Rectangle类型)
Rectangle rectc(); // oops, default constructor NOT called 

统一初始化

初始化类对象有几种方式:初始化器方式,统一初始化方式、赋值声明方式等

如果初始化器只有一个参数,初始化可以调用变量赋值的方式,语法如下

class_name object_name = initialization_value;

在 C++ 中引入了统一初始化方式,其与函数式初始化相似,区别在于使用的是花括号{},而不是小括号()。在变量名与花括号之间可以可选使用 = 。语法如下

class_name object_name { value, value, value, ... }
class_name object_name = { value, value, value, ... } // 有无等号均可

一个完整的类初始化DEMO,包含四种初始化方式

// classes and uniform initialization
#include <iostream>
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;
}
// --------------
foo's circumference: 62.8319

构造器中的成员初始化

构造器初始化 class 其他成员有几种方式,直接赋值成员变量 或者 使用初始化列表。

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

// 方式1: 直接操作class 内部成员
Rectangle::Rectangle (int x, int y) { width=x; height=y; }

// 方式2: 使用 : 在参数与方法体 之间创建初始化列表
Rectangle::Rectangle (int x, int y) : width(x) { height=y; }

// 方式3: 同2
Rectangle::Rectangle (int x, int y) : width(x), height(y) { }

如果成员是基本类型,是否使用初始化列表的方式没有区别,因为基本类型无默认构造器
如果成员是class类型,必须在初始化列表中调用初始化,否则它会自动调用默认构造器。

成员为 class 的初始化 Demo

// member initialization
#include <iostream>
using namespace std;

class Circle {
    double radius;
  public:
    Circle(double r) : radius(r) { }
    double area() {return radius*radius*3.14159265;}
};

class Cylinder {
    Circle base;
    double height;
  public:
    Cylinder(double r, double h) : base (r), height(h) {}
    double volume() {return base.area() * height;}
};

int main () {
  Cylinder foo (10,20);

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

// -----------
foo's volume: 6283.19

// 注意 - 初始化方式可以使用同一初始化
Cylinder::Cylinder (double r, double h) : base{r}, height{h} { }

使用指针指向类

使用 class 定义的新类型可以和其他基本类型一样被使用,指针同样可以指向类对象的地址,指针访问对象内部成员使用箭头操作符(->)。

// pointer to classes example
#include <iostream>
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;
}

几种运算符说明

expression can be read as
*x pointed to by x
&x address of x
x.y member y of object x
x->y member y of object pointed to by x
(*x).y member y of object pointed to by x (equivalent to the previous one)
x[0] first object pointed to by x
x[1] second object pointed to by x
x[n] (n+1)th object pointed to by x

使用 struct 和 union 定义 class

类可以使用 struct 和 union 定义。

struct 定义类: 其成员变量默认访问权限为 public, class 默认为 private
union 定义类: 内部只能存储一个成员变量,访问权限为public,也可以有成员函数。

操作符重载

操作符本质上是系统内建的函数,C++ 中可以对常用操作符进行重载,来实现自定义的操作。格式

type operator sign (parameters) { /*... body ...*/ }

例如: 笛卡尔向量具有加法操作,使用 C++ 实现如下

// overloading operators example
#include <iostream>
using namespace std;

class CVector {
  public:
    int x,y;
    CVector () {}; // 默认初始化器
    CVector (int a,int b) : x(a), y(b) {} // 指定初始化器
    CVector operator + (const CVector&);  // 重写 + 运算符函数声明
};

CVector CVector::operator+ (const CVector& param) {
  CVector temp;
  temp.x = x + param.x;
  temp.y = y + param.y;
  return temp;
}

int main () {
  CVector foo (3,1);
  CVector bar (1,2);
  CVector result;
  result = foo + bar;
  cout << result.x << ',' << result.y << '\n';
  return 0;
}

// ----------------
4,3

// 注意 - 操作运算符重写后,显隐式调用均可
c = a + b;
c = a.operator+ (b)

重写操作符两种形式: 重写为成员函数(上)、重写为非成员函数(下)

// non-member operator overloads
#include <iostream>
using namespace std;

class CVector {
  public:
    int x,y;
    CVector () {}
    CVector (int a, int b) : x(a), y(b) {}
};


CVector operator+ (const CVector& lhs, const CVector& rhs) {
  CVector temp;
  temp.x = lhs.x + rhs.x;
  temp.y = lhs.y + rhs.y;
  return temp;
}

int main () {
  CVector foo (3,1);
  CVector bar (1,2);
  CVector result;
  result = foo + bar;
  cout << result.x << ',' << result.y << '\n';
  return 0;
}

this 关键字

this 关键字用来指示调用函数的当前对象的地址(当前对象的指针)。

// example on this
#include <iostream>
using namespace std;

class Dummy {
  public:
    bool isitme (Dummy& param);
};

bool Dummy::isitme (Dummy& param)
{
  if (&param == this) return true;
  else return false;
}

int main () {
  Dummy a;
  Dummy* b = &a;
  if ( b->isitme(a) )
    cout << "yes, &a is b\n";
  return 0;
}

this 也常用于通过引用返回对象的赋值运算函数中,如下(重写了笛卡尔向量的赋值操作)

CVector& CVector::operator= (const CVector& param)
{
  x=param.x;
  y=param.y;
  return *this;
}

事实上,此 operator= 函数已经非常接近系统隐式生成的 operator= 代码了。

类中的静态成员

class 可以包含静态成员(成员变量 、函数)。关键字: static

静态成员变量全局只有一份内存,就像类的变量,可以直接用类名访问,所有的类对象访问其静态变量都是同一份内存。正是由于静态变量全局只有一份内存,所以其不能在class内部进行初始化,防止在创建新对象的时候多次初始化赋值。需要在class外部初始化一次即可。

// static members in classes
#include <iostream>
using namespace std;

class Dummy {
  public:
    static int n;
    Dummy () { n++; };
};

int Dummy::n=0;  // 静态变量,在class 初始化一次即可

int main () {
  Dummy a;
  Dummy b[5];
  cout << a.n << '\n';
  Dummy * c = new Dummy;
  cout << Dummy::n << '\n';
  delete c;
  return 0;
}

// -------------,事实证明每次构造新对象,内部全局变量都是同一份内存
6
7

静态函数: 同样可以看做是外部函数,其调用方式:直接用类名调用,函数内部不能访问类实例变量,也不能使用功能this指针。

常量类型的成员函数

当一个类的对象被声明为常量: const MyClass myobject; 该对象就可以当做一个只读对象,对于其所有成员(实例变量/函数)都会被限制成只读。但是类的构造器函数是正常调用的。

// constructor on const object
#include <iostream>
using namespace std;

class MyClass {
  public:
    int x;
    MyClass(int val) : x(val) {}
    int get() {return x;}
};

int main() {
  const MyClass foo(10);
// foo.x = 20;            // not valid: x cannot be modified
  cout << foo.x << '\n';  // ok: data member x can be read
  return 0;
}

// ------------
10

被修饰为常量的对象,可以调用的函数,也只能是被标记了常量函数的。格式为在函数名之后,方法体之前添加 const 修饰符,举例如下:

int get() const {return x;}        // const member function
const int& get() {return x;}       // member function returning a const&
const int& get() const {return x;} // const member function returning a const&

常量对象: 我们很少这样定义,主要使用场景为函数的参数
常量函数: 函数是否为常量会被重载为不同函数,系统会根据对象是否为常量访问到正确的函数

// overloading members on constness
#include <iostream>
using namespace std;

class MyClass {
    int x;
  public:
    MyClass(int val) : x(val) {}
    const int& get() const {return x;}
    int& get() {return x;}
};

int main() {
  MyClass foo (10);
  const MyClass bar (20);
  foo.get() = 15;         // ok: get() returns int&
// bar.get() = 25;        // not valid: get() returns const int&
  cout << foo.get() << '\n';
  cout << bar.get() << '\n';

  return 0;
}

// ----------------
15
20

类模板 - Class templates

同函数模板,类也可以使用模板,模板类允许类内部使用模板参数,模板格式template <class T>;

模板类的成员函数,如果在类外部实现,那么函数也需要声明template <...> 前缀。

// class templates
#include <iostream>
using namespace std;

template <class T>
class mypair {
    T a, b;
  public:
    mypair (T first, T second)
      {a=first; b=second;}
    T getmax ();
};

template <class T>
T mypair<T>::getmax ()  // 注意函数名中<T>,它标明了模板函数和模板类的参数。
{
  T retval;
  retval = a>b? a : b;
  return retval;
}

int main () {
  mypair <int> myobject (100, 75);
  cout << myobject.getmax();
  return 0;
} 

// -----------
100

如上: 使用模板类保存一对 T 类型元素,构造函数定义在内部,外部提供了一个获取最大值的函数,外部实现需要添加 template <class T> 函数前缀。

专业化模板类

对于模板函数,如果传入某种特定类型,实现了特定处理,这就叫做专业化模板类。同通用模板区别如下

template <class T> class mycontainer { ... };   // 通用模板
template <> class mycontainer <char> { ... };   // 专业模板

注意: 两种模板函数,没有继承关系. 如下代码中是完全两个类。

// template specialization
#include <iostream>
using namespace std;

// class template:
template <class T>
class mycontainer {
    T element;
  public:
    mycontainer (T arg) {element=arg;}
    T increase () {return ++element;}
};

// class template specialization:
template <>
class mycontainer <char> {
    char element;
  public:
    mycontainer (char arg) {element=arg;}
    char uppercase ()
    {
      if ((element>='a')&&(element<='z'))
      element+='A'-'a';
      return element;
    }
};

int main () {
  mycontainer<int> myint (7);
  mycontainer<char> mychar ('j');
  cout << myint.increase() << endl;
  cout << mychar.uppercase() << endl;
  return 0;
}

// ------
8
J

类的特殊成员函数

Class 中有六种特殊成员函数,如下

成员函数 形式: C 为类 隐式生成条件 默认操作
默认构造器: Default constructor C::C(); 无其他构造器
析构函数: Destructor C::~C(); 析构函数
拷贝构造器: Copy constructor C::C (const C&); 无移动构造和无移动赋值 copy所有成员
拷贝赋值: Copy assignment C& operator= (const C&); 无移动构造和无移动赋值 copy所有成员
移动构造器: Move constructor C::C (C&&); 无析构函数、无复制构造、无复制和移动赋值(即仅有构造函数) 移动所有成员
移动赋值: Move assignment C& operator= (C&&); 无析构函数、无复制构造、无复制和移动赋值(即仅有构造函数) 移动所有成员

默认构造器 与 自定义构造器

如果定义一个类,类中没有声明构造器,编译器会隐式生成一个默认构造器,默认构造器无参数。
如果有自定义的构造器,则不会隐式生成默认构造器,所以通常如果写构造器就要写两种:默认和自定义

析构函数
析构函数: 内部主要释放对象管理内存的成员。如对象内部有其他对象。

拷贝构造器
指的是使用该类对象作为参数的构造函数。 如B b = a;

拷贝赋值
重载等号 = , 使用该类对象赋值给已定义对象。 如b = a;

移动构造器
构造对象使用函数返回值的方式,如 B b = B(); B() 是一个返回值为 B& 的函数

移动赋值函数
移动赋值,类似拷贝赋值、右侧参数为未使用对象

// destructors
#include <iostream>
#include <string>
using namespace std;

class Example4 {
    string* ptr;
  public:
    // constructors:
    Example4() : ptr(new string) {}
    Example4 (const string& str) : ptr(new string(str)) {}
    // destructor:
    ~Example4 () {delete ptr;}
    
    // copy constructor: 深拷贝,隐式生成的浅拷贝
    Example4 (const Example4& x) : ptr(new string(x.content())) {}
    // copy assignment: 
    Example4& operator= (const Example4& x) {
      *ptr = x.content();
      return *this;
    }
    
    // move constructor
    Example4 (Example4&& x) : ptr(x.ptr) {x.ptr=nullptr;}
    // move assignment
    Example4& operator= (Example4&& x) {
      delete ptr; 
      ptr = x.ptr;
      x.ptr=nullptr;
      return *this;
    }
    // access content:
    const string& content() const {return *ptr;}
};

int main () {
  Example4 foo;
  Example4 bar ("Example");
  return 0;
}

看一下平常使用的赋值其本质的操作如下:

MyClass fn();            // function returning a MyClass object
MyClass foo;             // default constructor
MyClass bar = foo;       // copy constructor
MyClass baz = fn();      // move constructor
foo = bar;               // copy assignment
baz = MyClass();         // move assignment 

default 和 delete 关键字

C++ 有 default 和 delete 关键字来告诉编译器使用还是删除上述特殊函数。语法: 在函数声明后面添加 default / delete 关键字即可

struct Test {
    // 使用默认构造函数
    Test() = default;
    // 删除复制赋值运算符
    Test& operator=(const Test& test) = delete;
    // 使用默认析构函数
    ~Test() = default;
};

朋友关系 和 继承关系

Class 中成员默认访问级别为 private。 意味着在此类之外无法访问类成员。

朋友: 是用 friend 关键字声明的类或函数。朋友函数能在类外访问其成员。但是朋友的朋友不具备此能力。

原则就是: 在类中定义的 friend 函数/类 可以在外部访问类内部成员。谁被friend修饰,谁有此权限

朋友函数示例:

// friend functions
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle() {}
    Rectangle (int x, int y) : width(x), height(y) {}
    int area() {return width * height;}
    friend Rectangle duplicate (const Rectangle&);
};

// 此函数为朋友函数,可以访问参数类内部成员
Rectangle duplicate (const Rectangle& param)
{
  Rectangle res;
  res.width = param.width*2;
  res.height = param.height*2;
  return res;
}

int main () {
  Rectangle foo;
  Rectangle bar (2,3);
  foo = duplicate (bar);
  cout << foo.area() << '\n';
  return 0;
}

朋友类示例:

// friend class
#include <iostream>
using namespace std;

// 声明存在该类,后面都可以使用该类名
class Square;

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

class Square {
  // Rectangle 被声明为朋友类,代表 Rectangle 内部可以访问当前类的私有成员
  friend class Rectangle;
  private:
    int side;
  public:
    Square (int a) : side(a) {}
};

void Rectangle::convert (Square a) {
  width = a.side;
  height = a.side;
}
  
int main () {
  Rectangle rect;
  Square sqr (4);
  rect.convert(sqr);
  cout << rect.area();
  return 0;
}

继承关系

类之间可以继承,形成基类和子类的关系。
子类会继承父类的所有的成员。
子类可以自己定义自己新的成员、可以自定义实现父类的函数。

语法:

class derived_class_name: public base_class_name
{ /*...*/ };

DEMO

// derived classes
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b;}
 };

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

class Triangle: public Polygon {
  public:
    int area ()
      { return width * height / 2; }
  };
  
int main () {
  Rectangle rect;
  Triangle trgl;
  rect.set_values (4,5);
  trgl.set_values (4,5);
  cout << rect.area() << '\n';
  cout << trgl.area() << '\n';
  return 0;
}

// --------
20
10

Rectangle 和 Triangle 分别继承基类 Polygon. 其内部成员 width, height, set_values 也都继承了。

继承语法中冒号后的 public 限制了继承时候对父类成员的访问级别。建议使用public,不同关键字对子类继承的成员访问权限如下:

Access public protected private
基类的对象内部 yes yes Yes
子类对象内部 yes yes no
外界(其他函数、类) yes no no

继承语法三个关键字: public、protected、private。作用如下
public : 子类完整按照父类的成员本身的访问继承
protected : 子类继承的成员访问级别为 public 的会降级为 protected
private : 子类继承的成员都会降级为 private

继承关系继承不到的

子类虽然能继承父类成员,但是以下几种情况是无法继承的

  • 父类的构造函数和析构函数
  • 其分配运算符成员(operator=)
  • 父类内的朋友成员
  • 父类内部 private 访问属性的成员

子类的构造方法会调用父类的默认构造器。也可以通过以下语法调用指定的父类构造器。
指定调用父类构造函数derived_constructor_name (parameters) : base_constructor_name (parameters) {...}

// constructors and derived classes
#include <iostream>
using namespace std;

class Mother {
  public:
    Mother ()
      { cout << "Mother: no parameters\n"; }
    Mother (int a)
      { cout << "Mother: int parameter\n"; }
};

class Daughter : public Mother {
  public:
    // 子类构造函数没有指定特定父类构造器: 调用父类默认构造器
    Daughter (int a)
      { cout << "Daughter: int parameter\n\n"; }
};

class Son : public Mother {
  public:
    // 指定父类构造器: 会调用父类指定构造器
    Son (int a) : Mother (a)
      { cout << "Son: int parameter\n\n"; }
};

int main () {
  Daughter kelly(0);
  Son bud(0);
  
  return 0;
}

// -----------
Mother: no parameters
Daughter: int parameter

Mother: int parameter
Son: int parameter

类的多继承

C++ 中类的继承是可以多继承: 即同时有多个父类。语法即在继承的冒号之后用分号列举父类列表。一个完整的例子如下:

// multiple inheritance
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    Polygon (int a, int b) : width(a), height(b) {}
};

class Output {
  public:
    static void print (int i);
};

void Output::print (int i) {
  cout << i << '\n';
}

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

class Triangle: public Polygon, public Output {
  public:
    Triangle (int a, int b) : Polygon(a,b) {}
    int area ()
      { return width*height/2; }
};
  
int main () {
  Rectangle rect (4,5);
  Triangle trgl (4,5);
  rect.print (rect.area());
  Triangle::print (trgl.area());
  return 0;
}

// -----------
20
10 

C++ 中类的多态

多态: 即父类指针指向子类对象,指针调用对象方法的时候、编译器能找到正确的对象类型并正确调用到真实子类的对象方法。

C++ 中的多态有一些特殊的关键字: virtual。
基类定义的成员是通用成员,子类继承后一般会进行功能扩展。指向基类的指针无法直接访问子类成员。为了使基类更加通用,可以在基类中使用 virtual 声明子类的成员。

virtual 作用即定义基类中可以访问的子类内容。

// virtual members
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    virtual int area ()
      { return 0; }
};

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

class Triangle: public Polygon {
  public:
    int area ()
      { return (width * height / 2); }
};

int main () {
  Rectangle rect;
  Triangle trgl;
  Polygon poly;
  Polygon * ppoly1 = &rect;
  Polygon * ppoly2 = &trgl;
  Polygon * ppoly3 = &poly;
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  ppoly3->set_values (4,5);
  cout << ppoly1->area() << '\n';
  cout << ppoly2->area() << '\n';
  cout << ppoly3->area() << '\n';
  return 0;
}

// -----------
20
10
0

如上: 基类中 area() 成员是 virtual 修饰的,所以指向基类指针可以直接调用此方法。否则 area() 方法只能在子类指针的访问。

抽象基类

C++ 中提供抽象类的概念,其只能做为基类。类似上面示例,基类中提供 virtual 成员,语法为成员函数声明的定义直接 =0;

抽象类无法声明自己对象,但是可以声明指向该类型的指针。

// abstract class CPolygon
class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    // 注意: 只要包含一个可见成员,该类就成为抽象类
    virtual int area () =0;
    // 抽象基类中,其他成员可以使用 this关键字访问可见成员。(系统会访问到真实子类成员)
    void printarea()
      { cout << this->area() << '\n'; }
};

正是由于可见成员和抽象类构成了 C++ 的多态特性,下面是一个完整包含动态内存分配类构造初始化器多态 的示例

// dynamic allocation and polymorphism
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    Polygon (int a, int b) : width(a), height(b) {}
    virtual int area (void) =0;
    void printarea()
      { cout << this->area() << '\n'; }
};

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

class Triangle: public Polygon {
  public:
    Triangle(int a,int b) : Polygon(a,b) {}
    int area()
      { return width*height/2; }
};

int main () {
  Polygon * ppoly1 = new Rectangle (4,5);   // 直接将子类对象赋值给父类指针(多态)、动态内存分配
  Polygon * ppoly2 = new Triangle (4,5);
  ppoly1->printarea();
  ppoly2->printarea();
  delete ppoly1;
  delete ppoly2;
  return 0;
}

end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349