第六章 类
一。
1.类
类是对具有相同属性和行为的一组对象的抽象与统一描述。是用户自定义的数据类型
●类的定义包括行为和属性两个部分。
● 属性以数据表示,行为通过函数实现。
class 类名
{
public:
公有数据成员和成员函数;
protected:
保护数据成员和成员函数;
private:
私有数据成员和成员函数;
};
各成员函数的实现;
C++类定义的格式:
分号不得省略!!!!
class Student //定义学生类Student
{
public: //声明类成员
void Getinfo(string pname, string pid
,char Sex, int a,double s);
void modify(float s);
void display();
private:
string name;
stringid;
char sex;
int age;
double score;
}; //类定义以分号结束
●类的成员可以是其他类的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。
●类定义必须以分号“;”结束。
●类与结构体的区别:
没有明确指定类成员的访问权限时,C++结构体的成员是公有的,而类的成员是私有的。
2 . 访问控制
类成员访问控制
Public:公有成员 类的外部接口
Protected:保护成员 仅允许本类成员函数及派生类成员函数访问
Private:私有成员 仅允许本类成员函数访问
3 .成员函数
类的成员函数是实现类的行为属性的成员。
一般将成员函数声明为函数原型,在类外具体实现成员函数。
返回值类型 类名::成员函数名(参数表)
{
函数体
}
4. 对象
●对象是类的实例或实体。
●类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
●对象是类的实例或实体。
●类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
●必须在定义了类之后,才可以定义类的对象。
5 .类成员的访问
对象成员的访问包括:
●圆点访问形式:对象名.公有成员
●指针访问形式
int main()
{
float a1,a2;
ptr_access *ptr=new ptr_access;
ptr->setvalue(2,8);
//通过指针访问公有成员函数
ptr->print();
a1=(*ptr).Getx();
//通过公有成员函数访问私有数据成员
a2=(*ptr).Gety();
cout<<"a1="<<a1<<endl;
cout<<"a2="<<a2<<endl;
return 0;
- 类定义和使用时应注意:在类的定义中不能对数据成员进行初始化。
- 类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,将成员函数定义为公有成员。
- 类中的数据成员可以是C++语法规定的任意数据类型。
- 类的成员可以是其他类的对象,称为类的组合。但不能以类自身的对象作为本类的成员。
- 类定义必须以分号“;”结束
- class与struct的不同:
① class中,成员缺省情况是private。
② struct中,成员缺省情况是public。
}
6.构造函数和析构函数
Ø 构造函数是用于创建对象的特殊成员函数
当创建对象时,系统自动调用构造函数
Ø 构造函数的作用是:
为对象分配空间;对数据成员赋初值;请求其他资源
Ø 没有用户定义的构造函数时,系统提供缺省版本的构造函数
Ø 构造函数名与类名相同:类名
Ø 构造函数可以重载
Ø 构造函数可以有任意类型的参数,但没有返回类型
如果类中没有定义构造函数,系统将自动生成一个默认形式的构造函数,用于创建对象,默认构造函数形式:
类名::类名(){}
默认构造函数是一个空函数
为类Date建立一个构造函数。
为类Date建立一个构造函数。
#include <iostream.h>
class Date {
public:
Date(); // 无参构造函数
Date(int y,int m,int d);
void showDate();
private:
int year, month, day;
};
Date::Date() // 构造函数的实现
{ year=0; month=0; day=0; }
Date::Date(int y,int m,int d)
{ year=y; month=m; day=d; }
inline void Date::showDate()
{ cout<<year<<"."<<month<<"."<<day<<endl;}
int main()
{
Date a_date,bDate(2014,3,25);
a_date.showDate();
b_date.showDate();
return 0;
}
通常,利用构造函数创建对象有以下两种方法
(1) 利用构造函数直接创建对象.其一般形式为:
类名 对象名[(实参表)];
这里的“类名”与构造函数名相同,“实参表”是为构造函数提供的实际参数。
(2) 利用构造函数创建对象时,通过指针和new来实现。其一般语法形式为:
类名 *指针变量 = new 类名[(实参表)];
例如:
Date *date1=new Date(1998,4,28);
就创建了对象(*date1)。
构造函数的初始化列表
--------数据成员的初始化
n A.使用构造函数的函数体进行初始化
n class Date
n {
n int d, m, y;
n public:
n Date(int dd, int mm, int yy)
n {
n d=dd;
n m=mm;
n y=yy;
n }
n Date(int dd, int mm)
n {
n d=dd;
n m=mm;
n }
n }
n B.使用构造函数的初始化列表进行初始化
n 格式:
n funname(参数列表):初始化列表
n { 函数体,可以是空函数体 }
n 初始化列表的形式:
n 成员名1(形参名1),成员名2(形参名2),成员名n(形参名n)
必须使用参数初始化列表对数据成员进行初始化的几种情况
- 数据成员为常量
- 数据成员为引用类型
- 数据成员为没有无参构造函数的类的对象
类成员的初始化的顺序:
按照数据成员在类中的声明顺序进行初始化,与初始化成员列表中出现的顺序无关
析构函数
对象生存期结束时,需要做清理工作,比如:释放成员(指针)所占有的存储空间
析构函数可以完成上述工作。
析构函数自动调用(隐式调用)
析构函数没有返回值,不能有参数,也不能重载
定义格式如下(类外实现):
类名::~类名()
{
函数语句
}
析构函数有以下一些特点:
① 析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);
② 析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;
③ 当撤消对象时,编译系统会自动地调用析构函数。
默认析构函数
若没有显式定义析构函数,则系统自动生成一个默认形式的析构函数。
系统自动生成的默认构造函数形式如下:
类名::~类名(){}
一般情况下,可以不定义析构函数
但如果类的数据成员中包含指针变量是从堆上进行存储空间分配的话,需要在析构函数中进行存储空间的回收
7.this指针
用类去定义对象时,系统会为每一个对象分配存储空间。如果一个类包括了数据和函数,要分别为数据和函数的代码分配存储空间。按理说,如果用同一个类定义了10个对象,那么就需要分别为10个对象的数据和函数代码分配存储单元
需要显式引用this指针的三种情况
(1)在类的非静态成员函数中返回类对象本身或对象的引用的时候,直接使用return *this,返回本对象的地址时,return this。
(2)当参数与成员变量名相同时,如this->x = x,不能写成x = x。
(3)避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针。
复制构造函数
复制构造函数用一个已有同类对象创建新对象进行数据初始化
C++为类提供默认版本的复制构造函数
程序员可以定义用户版本的复制构造函数
语法形式
类名 :: 类名(const 类名 & 引用名 , …);
以下三种情况下由编译系统自动调用:
- 声明语句中用类的一个已知对象初始化该类的另一个对象时。
- 当对象作为一个函数实参传递给函数的形参时,需要将实参对象去初始化形参对象时,需要调用复制构造函数。
- 当对象是函数的返回值时,由于需要生成一个临时对象作为函数返回结果,系统需要将临时对象的值初始化另一个对象,需要调用复制构造函数。
8. 类的其他成员
常数据成员是指数据成员在实例化被初始化后,其值不能改变。
在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数
常对象
如果在说明对象时用const修饰,则被说明的对象为常对象。
常对象的说明形式如下:
类名 const 对象名[(参数表)];
或者
const 类名 对象名[(参数表)];
在定义常对象时必须进行初始化,而且不能被更新。
说明:
(1)C++不允许直接或间接更改常对象的数据成员。
(2)C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。
常成员函数
在类中使用关键字const说明的函数为常成员函数,常成员函数的说明格式如下:
类型说明符 函数名(参数表) const;
const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。
常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)
静态成员
• 静态成员不属于某一个单独的对象,而是为类的所有对象所共有
• 静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员: 保证在不依赖于某个对象的情况下,访问静态数据成员
• (1)静态成员函数在类外定义时不用static前缀。
• (2)静态成员函数主要用来访问同一类中的静态数据成员。
• (3) 私有静态成员函数不能在类外部或用对象访问。
• (4)可以在建立对象之前处理静态数据成员。
• (5)编译系统将静态成员函数限定为内部连接(在其他文件中不可见)。
• (6)静态成员函数中是没有this指针的。
• (7)静态成员函数不访问类中的非静态数据成员。如有需要,只能通过对象名(或指向对象的指针)访问该对象的非静态成员。
静态成员函数来访问非静态数据成员
#include<iostream.h>
class small_cat{
public:
small_cat(doublew){weight=w;total_weight+=w;total_number++;}
static void display(small_cat&w)
{cout<<"The small_catweights "<<w.weight<<"kg\n";}
static void total_disp()
{cout<<total_number<<"small_cattotal weight ";
cout<<total_weight<<"kg "<<endl;}
private:
double weight;
static double total_weight; static double total_number;
};
double small_cat::total_weight=0;double small_cat::total_number=0;
int main()
{ small_catw1(0.9),w2(0.8),w3(0.7);
small_cat::display(w1);small_cat::display(w2);
small_cat::display(w3);small_cat::total_disp();
return 0;}
2. 友元函数
如果在本类(类A)以外的其他地方定义了一个函数(函数B)
这个函数可以是不属于任何类的非成员函数,
也可以是其他类的成员函数,
在类体中用friend对其(函数B)进行声明,此函数就称为本类(类A)的友元函数。
友元函数(函数B)可以访问这个类(类A)中的私有成员
8.类的包含(类的组合)
Ø 类的包含是程序设计中一种软件重用技术。即定义一个新的类时,通过编译器把另一个类“抄”进来。
Ø 当一个类中含有已经定义的类类型成员,带参数的构造函数对数据成员初始化,须使用初始化语法形式。
构造函数 ( 形参表 ) : 对象成员1(形参表) , … , 对象成员n (形参表 ) ;
用类包含求计算两点之间的距离
#include<cmath>
#include<iostream>
using namespace std;
class Point
{ public:
Point( int xi=0, int yi=0 ) { x = xi; y = yi; }
int GetX() { return x; }
int GetY() { return y; }
private: int x; int y;
};
对象数组的初始化
1. 当对象数组所属的类中包含带参的构造函数,可用初始化列表完成对象数组的初始化。
2. 当对象数组所属的类中包含无参的构造函数,也可以先定义,再给每个数组元素赋值
3. 当对象数组所属的类中包含单个参数的构造函数,可简写。
二。
写一个程序,输入矩形的长和宽,输出面积和周长
class CRectangle
{
public:
intw,h;
intArea() {
returnw * h;
}
intPerimeter() {
return2 * ( w + h);
}
voidInit( int w_,int h_ ) {
w= w_; h = h_;
}
}; //必须有分号
int main( )
{
int w,h;
CRectangle r; //r是一个对象
cin >> w >> h;
r.Init( w,h);
cout << r.Area() << endl <<r. Perimeter();
return 0;
}
重载示例:
参数个数不同
#include<iostream>
using namespace std ;
int max ( int a , int b ) ;
int max ( int a , int b, int c ) ;
int main ()
{cout << max ( 5, 3 ) << endl ;
cout << max (4, 8, 2 ) << endl ;
}
int max ( int a , int b )
{return a > b ? a : b ; }
int max ( int a , int b, int c )
{int t ;
t = max ( a , b ) ;
return max ( t , c ) ;
}
参数个数相同
参数类型不同
#include<iostream>
using namespace std ;
int abs ( int a ) ;
double abs ( double f ) ;
int main ()
{ cout << abs ( -5 ) << endl ;
cout << abs ( -7.8 ) << endl ;
}
int abs ( int a )
{ return a < 0 ? -a : a ; }
double abs ( double f )
{ return f < 0 ? -f : f ; }
//带参数构造函数完成数据成员初始化
Student::Student( int y, int m, int d,int num, string name ) : code( num )
{ this->name=name;
birthday.year=y;
birthday.month=m;
birthday.day=d;
}
int main()
{ Student stu1( 1990, 3, 21, 1001,"陈春“ );
stu1.PrintStudent();
Student stu2(1985, 10, 1, 1002, "张庆华“ );
stu2.PrintStudent();
}
三。
学到类才意味着接触到c++的门槛,但大概因为刚开始学习,c++和上学期学习的c语言在思考观念上有很的不同,比起c语言的的直来直往,感觉c++的思考方式更加复杂,所以感觉学起来有些吃力,一般的理论知识可以懂个大概,但是一到运用上就有些有心无力。所以呢,在未来的学习中,一方面要加强对于基础的理解,令一方重要的是要转变对于c++做题中的观念,尽早学会用C++的思考方式去解决问题,虽然这过程对于自己来说会是个挑战,但自己一定会会努力的!
| ||
|
//带参数构造函数完成数据成员初始化
Student::Student( int y, int m, int d,int num, string name ) : code( num )
{ this->name=name;
birthday.year=y;
birthday.month=m;
birthday.day=d;
}
int main()
{ Student stu1( 1990, 3, 21, 1001,"陈春“ );
stu1.PrintStudent();
Student stu2(1985, 10, 1, 1002, "张庆华“ );
stu2.PrintStudent();
}
| ||
|
int main( )
{
intw
cin >> w >> h;
r.Init( w,h)
cout << r.Area() << endl<< r. Perimeter();
return 0;
}
写一个程序,输入矩形的长和宽,输出面积和周长
| ||
|