前言
经过四篇散文,终于接触到了指针相关知识点我不会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
};
访问数据成员
类的对象的公共数据成员可以使用直接成员访问操作符 . 访问.
类与对象的细节
继承
在面向对象编程中最重要的概念之一就是继承。继承允许我们根据一个类来定义另一个类,这使得创建和维护一
个应用程序更加的容易。这也提供了一个重用代码功能和快速实现的机会。
当创建一个类,不是写全新的数据成员和成员函数的时候,程序员可以指定新类,这个类可以继承现有类的成
员。这个现有的类称为基类,这个新类称为派生类。
继承的概念其实是一种关系。例如,哺乳动物是动物,狗是哺乳动物,因此狗是动物等等。
基类和派生类
一个类可以继承多个类,这就意味着它可以从多个基类中继承数据和函数。为了定义一个派生类,我们可以使用
一个类继承列表来指定基类。一个类继承列表指定一个或多个基类,类继承列表形式如下:
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
可重载/不可重载的运算符
不可以重载的运算符
多态
面向对象非常重要的一个概念:多态性意味着有多种形式。通常,多态发生在类之间存在层级关系且这些类有继承关系的时候。 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
就是告诉编译器上面的函数没有函数体。上面的虚函数就被称为纯虚函数。