我不会C++,没问题,跟凯哥一起学(五)

前言

经过四篇散文,终于接触到了指针相关知识点我不会C++,没问题,跟凯哥一起学(四)接下来我们继续:

访问结构体成员

访问一个结构体的任何成员,我们使用 member access operator(成员访问操作符):(.) 来访问结构体成 员。成员访问操作符编码为结构变量名和我们要访问结构成员之间的一个点符号。使用关键字 struct 来定义结 构类型的变量。

#include <iostream>
#include <cstring>
using namespace std;
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
int main( ) {
   struct Books Book1;// Declare Book1 of type Book
   struct Books Book2;// Declare Book2 of type Book
   // book 1 specification
   strcpy( Book1.title, "Learn C++ Programming");
   strcpy( Book1.author, "Chand Miyan");
   strcpy( Book1.subject, "C++ Programming");
   Book1.book_id = 6495407;
   // book 2 specification
   strcpy( Book2.title, "Telecom Billing");
   strcpy( Book2.author, "Yakit Singha");
   strcpy( Book2.subject, "Telecom");
   Book2.book_id = 6495700;
   // Print Book1 info
   cout << "Book 1 title : " << Book1.title <<endl;
   cout << "Book 1 author : " << Book1.author <<endl;
   cout << "Book 1 subject : " << Book1.subject <<endl;
   cout << "Book 1 id : " << Book1.book_id <<endl;
// Print Book2 info
cout << "Book 2 title : " << Book2.title <<endl;
   cout << "Book 2 author : " << Book2.author <<endl;
   cout << "Book 2 subject : " << Book2.subject <<endl;
   cout << "Book 2 id : " << Book2.book_id <<endl;
return 0; }
编译和执行上面的代码,执行结果如下:
Book 1 title : Learn C++ Programming
Book 1 author : Chand Miyan
Book 1 subject : C++ Programming
Book 1 id : 6495407
Book 2 title : Telecom Billing
Book 2 author : Yakit Singha
Book 2 subject : Telecom
Book 2 id : 6495700

结构体指针

您可以定义结构体指针,以一种定义指向其他变量的指针非常相似的方式

struct Books *struct_pointer;

现在,您可以用上面定义的指针变量存储一个结构变量的地址。找到一个结构变量的地址,把操作符 & 置于结构 体名称的前面,如下所示:

struct_pointer = &Book1;

为了通过一个指向结构的指针访问结构体成员,必须使用 -> 操作符,如下所示:

struct_pointer->title;

让我们使用结构指针重写上面的例子,希望这将帮助你更容易理解这个概念:

#include <iostream>
#include <cstring>
using namespace std;
void printBook( struct Books *book );
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};
int main( ) {
   struct Books Book1;// Declare Book1 of type Book
   struct Books Book2;// Declare Book2 of type Book
   // Book 1 specification
   strcpy( Book1.title, "Learn C++ Programming");
   strcpy( Book1.author, "Chand Miyan");
   strcpy( Book1.subject, "C++ Programming");
   Book1.book_id = 6495407;
   // Book 2 specification
   strcpy( Book2.title, "Telecom Billing");
   strcpy( Book2.author, "Yakit Singha");
   strcpy( Book2.subject, "Telecom");
   Book2.book_id = 6495700;
   // Print Book1 info, passing address of structure
   printBook( &Book1 );
// Print Book1 info, passing address of structure
   printBook( &Book2 );
return 0; }
// This function accept pointer to structure as parameter.
void printBook( struct Books *book )
{
   cout << "Book title : " << book->title <<endl;
   cout << "Book author : " << book->author <<endl;
   cout << "Book subject : " << book->subject <<endl;
   cout << "Book id : " << book->book_id <<endl;
}
编译和执行上面的代码,执行结果如下:
Book title : Learn C++ Programming
Book author : Chand Miyan
Book subject : C++ Programming
Book id : 6495407
Book title : Telecom Billing
Book author : Yakit Singha
Book subject : Telecom
Book id : 6495700

struct和typedef struct的区别

在C语言中,结构体的定义要用typedef struct,例如:

typedef struct Number{
    int a;
    int b;
}Num;

这里的Number和Num指的都是整个结构体,在声明结构体变量的时候可以是

struct Number num1;  //声明结构体变量的第一种方式

也可以是

Num num1;           //声明结构体变量的第二种方式

也就是说Num==struct Number。定义中的Number其实也可以省略,这样的话声明变量就只能是第二种方式了:

typedef struct{     //去掉Number也对
    int a;
    int b;
}Num;
Num num1;   

在C++中,typedef的使用和C中是一样的:

typedef struct Number{      //去掉Number也对
    int a;
    int b;
}Num;                       //Num是一个结构体类型,Num=struct Number.                    
Num num1;                   //使用时需要先声明结构体变量
num1.a=10;                  //再访问成员变量

在C++中还有一种更简便的使用方法:

struct Number{
    int a;
    int b;
}Num;                       //Num是一个结构体变量!!!!               
Num.a=10;                   //使用时可以直接访问成员变量

终于进入对象环节了

这里大家简单过下,接触过java面向对象的同学这里可以一秒扫过,遇到问题再回头查验。

类和对象、C++ 类的定义

class Box {
   public:
  double length;   // Length of a box
  double breadth;  // Breadth of a box
  double height;   // Height of a box
};

访问数据成员

类的对象的公共数据成员可以使用直接成员访问操作符 . 访问.

类与对象的细节

类与对象的细节.png

继承

在面向对象编程中最重要的概念之一就是继承。继承允许我们根据一个类来定义另一个类,这使得创建和维护一
个应用程序更加的容易。这也提供了一个重用代码功能和快速实现的机会。
当创建一个类,不是写全新的数据成员和成员函数的时候,程序员可以指定新类,这个类可以继承现有类的成
员。这个现有的类称为基类,这个新类称为派生类。
继承的概念其实是一种关系。例如,哺乳动物是动物,狗是哺乳动物,因此狗是动物等等。

基类和派生类

一个类可以继承多个类,这就意味着它可以从多个基类中继承数据和函数。为了定义一个派生类,我们可以使用
一个类继承列表来指定基类。一个类继承列表指定一个或多个基类,类继承列表形式如下:

class derived-class: access-specifier base-class

在这里 access-specifier 是 public 、 protected 或者 private ,base-class 是之前定义的类的名称。如果 不使用 access-specifier ,那么在默认情况下它是私有的。
考虑一个基类的 shape 和其派生类 Rectangle 的继承情况如下:

#include <iostream>
using namespace std;
// Base class
class Shape
{
   public:
  void setWidth(int w)
  {
width = w; }
  void setHeight(int h)
{
height = h;
} protected:
  int width;
  int height;
};
// Derived class
class Rectangle: public Shape
{
   public:
  int getArea()
  {
 return (width * height);
  }
};
int main(void)
{
   Rectangle Rect;
   Rect.setWidth(5);
   Rect.setHeight(7);
   // Print the area of the object.
   cout << "Total area: " << Rect.getArea() << endl;
return 0; }
上面的代码编译和执行时,它产生以下结果:
Total area: 35

继承方式

当从一个基类派生一个子类的时候,公共基类可以通过 public ,protected ,或者 private 方式被继承。继承 方式被 access-specifier 指定,正如上面解释的。
我们几乎不使用protected或私有private 继承,但public继承是常用的。在使用不同类型的继承的时候,应用规 则如下:
• public 继承:当从一个公有基类派生一个类的时候,基类的公有成员成为派生类的公有成员;基类的保护成 员成为派生类的保护成员。一个基类的私有成员不能被派生类直接访问,但可以通过调用基类的公有和保护 成员访问基类的私有成员。
• protected 继承:当从一个受保护的基类派生子类的时候,基类的公有和保护成员成为派生类的保护成员。
• private 继承:当从一个私有的基类派生子类的时候,基类的公有和保护成员成为派生类的私有成员。

多继承

一个C++类可以继承多个类的成员,多继承语法如下:
class derived-class: access baseA, access baseB....
在这里 access 是 public ,protected ,或者是 private ,并且每一个基类将有一个 access 类型,他们将由 逗号分隔开,如上所示。让我们试试下面的例子:

#include <iostream>
using namespace std;
// Base class Shape
class Shape
{
   public:
  void setWidth(int w)
  {
width = w; }
  void setHeight(int h)
  {
  height = h; }
   protected:
  int width;
  int height;
};
// Base class PaintCost
class PaintCost
{
   public:
  int getCost(int area)
  {
 return area * 70;
  }
};
// Derived class
class Rectangle: public Shape, public PaintCost
{
   public:
  int getArea()
  {
 return (width * height);
  }
};
int main(void)
{
   Rectangle Rect;
   int area;
   Rect.setWidth(5);
   Rect.setHeight(7);
   area = Rect.getArea();
   // Print the area of the object.
   cout << "Total area: " << Rect.getArea() << endl;
   // Print the total cost of painting
   cout << "Total paint cost: $" << Rect.getCost(area) << endl;
return 0; }
上面的代码编译和执行时,它产生以下结果:
Total area: 35
Total paint cost: $2450

C++ 中的函数重载

C++ 允许在同一范围内对一个函数名或一个操作符指定多个定义,分别被称为函数重载和操作符重载。
重载声明是在同一的范围内对先前已经声明的相同函数名的声明,除非这两个声明有不同的参数和明显不同的定
义(实现方式)。
当你调用一个重载的函数或操作符时,编译器通过比较用来调用函数或操作符的指定的参数类型来确定使用最合
适的定义。选择最合适的重载函数或操作符的过程被称为重载决议。
你可以在同一范围内对同一函数名有多个定义。函数的定义必须满足参数类型不同或参数的数量不同或两者都不
相同。你不能重载只有返回类型不同的函数声明。

#include <iostream>
using namespace std;
class printData
{
   public:
  void print(int i) {
cout << "Printing int: " << i << endl;
  }
  void print(double  f) {
cout << "Printing float: " << f << endl;
}
  void print(char* c) {
cout << "Printing character: " << c << endl;
} };
int main(void)
{
   printData pd;
   // Call print to print integer
pd.print(5);
   // Call print to print float
   pd.print(500.263);
   // Call print to print character
   pd.print("Hello C++");
return 0; }
上面的代码编译和执行时,它产生以下结果:
Printing int: 5
Printing float: 500.263
Printing character: Hello C++

C++ 中的运算符重载

你可以重新定义或重载的大部分 C++ 已有的操作符。因此,程序员可以像使用用户自定义类型一样使用操作 符。
重载操作符是一类函数,它们就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类 型。像任何其它函数,重载运算符也有返回类型和参数列表。
C++ 中的运算符重载

Box operator+(const Box&);

声明加法运算符可以用来使两个 Box 对象相加并返回最终 Box 对象。大多数重载运算符可以被定义为普通非成 员函数或类成员函数。如果我们把上面的函数定义为一个类的非成员函数,那么我们就必须为每个操作数传两个参 数如下:

Box operator+(const Box&, const Box&);

下面是通过使用成员函数来展示运算符重载的概念的示例。这里一个对象作为一个参数被传递,通过访问这个对 象可以获得参数的属性,将调用这个操作符的对象可以通过使用 this 操作符获得,下面这个例子展示了这一 点:

#include <iostream>
using namespace std;
class Box {
public:
    double getVolume(void)
  {
 return length * breadth * height;
  }
  void setLength( double len )
  {
  length = len;
  }
  void setBreadth( double bre )
  {
  breadth = bre;
  }
  void setHeight( double hei )
  {
  height = hei;
  }
  // Overload + operator to add two Box objects.
  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;
} private:
  double length;  // Length of a box
  double breadth; // Breadth of a box
  double height;  // Height of a box
};
// Main function for the program
int main( )
{
   Box Box1;// Declare Box1 of type Box
   Box Box2;// Declare Box2 of type Box
   Box Box3;// Declare Box3 of type Box
   double volume = 0.0; // Store the volume of a box here
   // box 1 specification
   Box1.setLength(6.0);
   Box1.setBreadth(7.0);
   Box1.setHeight(5.0);
// box 2 specification
   Box2.setLength(12.0);
   Box2.setBreadth(13.0);
   Box2.setHeight(10.0);
   // volume of box 1
   volume = Box1.getVolume();
   cout << "Volume of Box1 : " << volume <<endl;
   // volume of box 2
   volume = Box2.getVolume();
   cout << "Volume of Box2 : " << volume <<endl;
   // Add two object as follows:
   Box3 = Box1 + Box2;
   // volume of box 3
   volume = Box3.getVolume();
   cout << "Volume of Box3 : " << volume <<endl;
return 0; }
上面的代码编译和执行时,它产生以下结果:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400

可重载/不可重载的运算符

可以重载的运算符.png

不可以重载的运算符

不可以重载的运算符.png

多态

面向对象非常重要的一个概念:多态性意味着有多种形式。通常,多态发生在类之间存在层级关系且这些类有继承关系的时候。 C++ 多态性是指不同的对象发送同一个消息,不同对象对应同一消息产生不同行为。 考虑下面的例子,一个基类派生了其他的两类:

#include <iostream>
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 :" <<endl;
 return 0;
} };
class Rectangle: public Shape{
   public:
  Rectangle( int a=0, int b=0):Shape(a, b) { }
  int area ()
  {
 cout << "Rectangle class area :" <<endl;
 return (width * height);
} };
class Triangle: public Shape{
   public:
  Triangle( int a=0, int b=0):Shape(a, b) { }
  int area ()
  {
 cout << "Triangle class area :" <<endl;
 return (width * height / 2);
}
};
// Main function for the program
int main( )
{
   Shape *shape;
   Rectangle rec(10,7);
   Triangle  tri(10,5);
   // store the address of Rectangle
   shape = &rec;
   // call rectangle area.
   shape->area();
   // store the address of Triangle
   shape = &tri;
   // call triangle area.
   shape->area();
return 0; }
上面的代码编译和执行时,它产生以下结果:
Parent class area
Parent class area

输出结果不正确的原因是对函数 area() 的调用被编译器设置了一次,即在基类中定义的版本,这被称为对函数 调用的静态分辨或者静态链接,静态链接就是在程序被执行之前函数调用是确定的。这有时也被称为早期绑 定,因为函数 area() 在编译程序期间是固定的。
但是现在,让我们对程序做略微修改,并在 Shape 类中 area() 的声明之前加关键字 virtual ,它看起来像这 样:

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 :" <<endl;
return 0; }
};
这轻微的修改后,前面的示例代码编译和执行时,它会产生以下结果:
Rectangle class area
Triangle class area

这一次,编译器关注的是指针的内容而不是它的类型。因此,由于三角形和矩形类对象的地址被存储在形状类 中,各自的 area() 函数可以被调用。
正如你所看到的,每个子类都有一个对 area() 函数的实现。通常多态就是这样使用的。你有不同的类,它们都 有一个的相同名字的函数,甚至有相同的参数,但是对这个函数有不同的实现。

虚函数

基类中的虚函数是一个使用关键字 virtual 声明的函数。派生类中已经对函数进行定义的情况下,定义一个基类 的虚函数,就是要告诉编译器我们不想对这个函数进行静态链接。
我们所希望的是根据调用函数的对象的类型对程序中在任何给定指针中被调用的函数的选择。这种操作被称为动
态链接,或者后期绑定。

纯虚函数

可能你想把虚函数包括在基类中,以便它可以在派生类中根据该类的对象对函数进行重新定义,但在许多情况下,在基类中不能对虚函数给出有意义的实现。
我们可以改变基类中的虚函数 area() 如下:

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

就是告诉编译器上面的函数没有函数体。上面的虚函数就被称为纯虚函数。

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