类与对象
类
定义
公有成员通常为成员函数
私有成员为数据成员
class 类名
{
public:
<数据成员或成员函数的说明>;
private:
<成员函数或数据成员的说明>;
protected:
<成员函数或数据成员的说明>;
};
<各个成员函数的实现>;
注: 1.成员函数可直接操作数据成员;若在函数内部定义与数据成员同名的变量,则操作的是函数内的局部变量 2.类体中不允许对数据成员的初始化 3.自身类的对象不可以作为自己的成员 4.类体内只给出成员函数的说明未给出具体实现时,则在实现部分成员函数名前需要加类名与作用域运算符(:: )
对象
定义
类名 对象名表;
加入Circle是前面已定义过的圆类,则可以定义为
Circle c1,*p,c[5];
对象的访问
对象成员的访问
对象.数据成员
对象.成员函数(参数表)
通过指向对象的指针访问成员
对象指针->数据成员
(*对象指针).数据成员
对象指针->成员函数(参数表)
(*对象指针).成员函数(参数表)
class Circle
{
private:
double x,y,r;
public:
void set(double x1,double y1,double r1);
void print();
double getarea();
double getgirth();
};
void Circle :: set(double x1,double y1,double r1)
{
x = x1;
y = y1;
r = r1;
}
double Circle :: getarea()
{ return 3.14*r*r;}
int main()
{
Circle c;
c.set(0,0,2);
c.print();
cout<<c.getarea()<<endl;
cout<<c.getgirth()<<endl;
return 0;
}
【问题】:定义一个职工类Staff,包含工号与工资两个数据成员以及设置和输出施工信息的功能,同时通过对象和指针对该类进行测试
#include<iostream>
#include<string.h>
using namespace std;
class Staff {
private:
char no[7];
float money;
public:
void display()
{
cout << no << ":" << money << endl;
}
void set(char n[], float w)
{
strcpy(no,n);
money = w;
}
};
int main()
{
Staff s, * s1;
s.set("10109",3526);
s.display();
s1 = new Staff;
s1->set("090303", 4003);
s1->display();
system("pause");
return 0;
}
构造函数与析构函数
构造函数
定义一个对象时,对系统而言就意味这要创建这个对象,这需要构造函数的指出; 另外,对象也可以初始化,这也需要构造函数的支持 特点:
- 是类的一个特殊成员函数,创建对象时需要调用该函数;
- 在创建对象时由系统自动调用,程序中不能直接调用,
- 函数名同类名,不能指定函数类型,可以带参数
- 可定义多个参数个数不同的构造函数,即构造函数允许重载
重载是c++允许在同一范围中声明几个功能类似的同名函数,但这些同名函数的形式参数的个数或类型不同
缺省构造函数
<构造函数名>()
{
}
拷贝构造函数
该函数只有一个参数,且是对某个对象的引用。
作用: 1.用已知的对象初始化被创建的同类对象 2.做函数的形参以及返回值时创建对象
如:前例的Circle类中隐含了缺省拷贝构造函数
Circle(Circle &c)
{x=c.x;y=c.y;r=c.r}
可以有定义
Circle c1(0,0,2),c2(c1);
问题:定义一个复数类,具有初始化复数(通过构造函数),输出复数以及求了两个复数和的功能
析构函数
当对象的生存期结束时,通过析构函数释放对象
- 当一个对象被定义在函数体内,则当该函数结束时,该对象的析构函数被自动调用,对象占用的空间被释放;
- 若一个对象是使用new运算符被动态创建,则在执行delete运算时,析构函数将会被自动调用,该对象占用的空间被释放;
特点: 1.是一个特殊的成员函数,用于释放对象,生存期结束时由系统自动调用,也可由用户调用; 2.函数名同类名,前面加字符"~" ,不指定类型,无参; 3.不可重载,即一个类只能定义一个析构函数。
缺省析构函数 若是用户没有定义过析构函数,系统会自动生成缺省形式的析构函数
~<析构函数名>()
{
}
类的特殊成员
常成员
保护数据和对象不被修改,可声明为常数据成员或常对象 与这些长数据或常对象相关的操作又需要常成员函数的支持
常数据成员
定义某数据成员时,类型前加const声明,可使得该数据受到强制保护。
常成员函数
不能修改数据成员的值,用于读取常对象的值
类型说明符 函数名(参数表) const
注: 1.不修改对象数据成员的成员函数才能声明为常成员函数; 2.构造函数和析构函数不能声明为const; 3.除构造函数和析构函数之外,只有常成员函数可以操作常对象
静态成员
为了解决数据共享问题,类中支持静态成员的定义,分为静态数据成员和静态函数
静态数据成员
数据成员定义前加关键字static说明:静态成员
- 静态成员是类的成员,在内存中只存贮一次,被所有对象共享
- 初始化的位置需放在类体外,初始化语句前不加static和访问权限修饰符
数据类型 类名::静态数据成员=值
静态成员函数
静态成员函数:成员函数声明前加static说明 调用形式
类名::静态成员函数名(参数表)
继承与多态
继承关系与派生类的定义
通过继承,原有类为基类(父类),新类为派生类(子类) C++支持单继承与多继承
单继承
一个派生类只有一个父类的继承关系
class 派生类名:继承方式 基类名
{派生类新定义成员};
- 继承方式:public、private、protected
- 派生类继承基类中除构造函数与析构函数以外的所有成员;
- 继承方式的不同决定了派生类对基类成员访问权限的不同,实际应用中都采用公有继承
** 问题**:假设Circle是已经定义过的类,从该类派生定义一个圆柱体类,具有数据成员的输入与输出功能
定义的圆类
class Cricle
{
private:
double r;
public:
void set(double r1)
{
r = r1;
}
void print()
{
cout << "半径:" << r << endl;
}
};
定义的Cylinder类
class Cylinder :public Cricle
{
private:
double h;
public:
void print()
{
Cricle::print();
cout << "圆柱高:" << h << endl;
}
void set(double r1, double h1)
{
Cricle::set(r1);
h = h1;
}
基类名::数据成员名;
基类名::成员函数名(参数表);
派生类对基类的访问
访问
问题:我们在上述的派生类Cylinder中增加求圆柱体体积的成员函数volume,如何实现?
构造函数与析构函数
- 派生类的构造函数要给自己的数据成员初始化,同时也要调用基类的构造函数使得基类的数据成员初始化;
- 当对象被删除时,派生类的析构函数被执行,同时基类的析构函数也将被调用
派生类名(总参数表):基类构造函数(参数表1)
{<派生类中数据成员初始化>};
- 基类中若有缺省的构造函数或没定义构造函数,则派生类构造函数的定义中可省略对基类构造函数的调用,而隐式调用缺省构造函数
- 基类构造函数中,只有有参的构造函数,则派生类构造函数中必须调用基类构造函数
- 派生类构造函数的调用顺序为先基类,再派生
- 派生类析构函数的执行顺序为先派生再基类
问题:在原有职工类的staff的基础上派生一个子类staff1,该类中增加工龄成员及按工龄涨工资的函数,每满10年涨80元
#include<iostream>
#include<string.h>
using namespace std;
class Staff
{
private:
char no[7];
float wage;
public:
void display()
{
cout << no << ":"<< wage << endl;
}
Staff(char n[], float w)
{
cout << "Staff构造函数被调用\n";
strcpy(no,n);
wage = w;
}
};
class Staff1 :public Staff
{
private:
int wt;
public:
Staff1(char n[], float w, int t) :Staff(n, w)
{
cout << "Staff1构造函数被调用\n";
wt = w;
}
double addwage()
{
return(wt / 10 * 80);
}
void display()
{
Staff::display();
cout << wt << "," << addwage() << endl;
}
};
int main()
{
Staff1 s("110109", 3526, 11);
s.display();
system("pause");
return 0;
}
在编写程序时,遇到了这么一个错误 在C++ 中,非定义与初始化时,双引号返回一个指向变量的地址,在上述主函数代码中Staff s("110109") 代表的是指向变量110109的地址,所以我们在定义字符串数组阶段,采用指针变量的形式进行定义,就解决了等号两边变量=与变量类型相同的值 的问题。具体参考自用01中的字符指针一节。 因此在定义Staff类保存工号的类型应定义为指针变量char *n; (我们尽量定义数组和使用数组时采用指针变量)
基于虚函数的多态性
向不同的对象发送同一个消息,不同对象会产生不同的响应,在程序中发出消息代表调用函数,而响应代表函数实现。通过多态性可实现“一个接口多种方法”。形式有:
函数重载与运算符重载都属于静态多态,而使用虚函数实现一种接口多种方法的是动态多态
虚函数
一种接口,多种方法
virtual 类型说明符 函数名(参数表)
虚函数使程序在运行阶段具体决定调用哪个类的方法(函数),而非按编译阶段绑定的基类方法执行,这成为动态联编
- 基类中有说明的虚函数,可以只将基类中的成员函数显式地说明为虚函数,而派生类中的同名函数也隐含为虚函数;
- 派生类的虚函数应与基类中的虚函数具有相同的名称、参数个数以及参数类型;
- 调用虚函数操作的只能是对象指针或对象引用,否则仍为静态联编
抽象类
抽象类带有纯虚函数的特殊类,主要作用是将有关的子类组织在一个继承层次结构中,由它来为他们提供一个公共的根
纯虚函数
virtual 类型 函数名(参数表)=0;
抽象类的特点
- 只能用作其他类的基类,不能创建抽象类对象;
- 可定义抽象类指针与引用,指向或引用其派生类,进而实现多态性
- 不能用作参数类型、函数返回类型或强制转换的类型。
#include<math.h>
#include<iostream>
using namespace std;
class base
{
protected:
int x, y;
public:
virtual void set(int i, int j = 0)
{
x = i;
y = j;
}
virtual void disp() = 0;
};
class square :public base
{
public:
void disp()
{
cout << x * x << endl;
}
};
class cube :public base
{
public:
void disp()
{
cout << x * x * x << endl;
}
};
class pow1 :public base
{
public:
void disp()
{
cout << pow((double)x, y);
}
};
int main()
{
base* ptr; square B; cube C; pow1 D;
ptr = &B;
ptr->set(5);
ptr->disp();
ptr = &C;
ptr->set(6);
ptr->disp();
ptr = &D;
ptr->set(3,4);
ptr->disp();
system("pause");
return 0;
}
可以将上述主函数的代码写在一个方法中
|