一.知识点总结
类
类的定义:⑴类是对具有相同属性和行为的一组对象的抽象与统一描述。是用户自定义的数据类型。
⑵类的定义包括行为和属性两个部分。
⑶属性以数据表示,行为通过函数实现。
格式:class 类名
{
public:公有数据成员和成员函数;
private: 私有数据成员和成员函数;
protected:保护数据成员和成员函数;
};//类的定于必须以分号结束!
例:学生信息系统
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++结构体的成员是公有的,而类的成员是私有的。
⑷c++类定义的格式:(数据成员私有化,成员函数公有化)
访问控制:
类的访问属性有:Public、Protected、Private
各种访问方式的功能如下:
访问控制 含义 属性
public 公有成员 类的外部接口
protected 保护成员 仅允许本类成员函数及派生类成员函数访问
private 私有成员 仅允许本类成员函数访问
成员函数:
类的成员函数是实现类的行为属性的成员。
一般将成员函数声明为函数原型,在类外具体实现成员函数。
成员函数的定语:
返回值类型 类名::成员函数名(参数表) //::作用域区分符,用于标识属于什么类的成员。
{
函数体
}
对象:
对象是类的实例或实体。
类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
对象的定义格式如下:
类名 对象名1,对象名2,…,对象名n;
定义对象应注意:必须在定义了类之后,才可以定义类的对象。
例:
class Point
{
public:
void InitPoint(float PointA_x, float PointA_y);
void Move(float New_x, float New_y);
float GetPointx();
float GetPointy();
private:
float P1_x,P1_y;
};
int main()
{
Point p1,p2;
}
类成员的访问
对象成员的访问包括:⑴圆点访问形式:对象名.公有成员
⑵指针访问形式
指针访问形式:对象指针变量名->公有成员
例:坐标点赋值
#include<iostream.h>
class ptr_access
{
public:
void setvalue(float a, float b) { x=a; y=b; }
float Getx() {return x;}
float Gety() {return y;}
void print()
{
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
}
private://私有数据成员
float x,y;
};
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;
}
内联函数
内联函数作用:减少频繁调用小子程序的运行的时间开销
内联函数机制:编译器在编译时,将内联函数的调用以相应代码代替
内联函数声明:inline 函数原型
注:内联函数仅在函数原型作一次声明。
适用于只有1 ~ 5行的小函数
不能含有复杂结构控制语句 ,不能递归调用
函数重载
定于:函数名相同,但参数不相同(类型不同,或者个数不同)的一组函数。
编译器根据不同参数的类型和个数产生调用匹配
函数重载用于处理不同数据类型的类似任务
例:参数个数相同,类型不同
#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 ;
}
例:参数个数不同
#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 ) ;
}
构造函数
定义:构造函数是用于创建对象的特殊成员函数。当创建对象时,系统自动调用构造函数。
作用:为对象分配空间;对数据成员赋初值;请求其他资源。
没有用户定义的构造函数时,系统提供缺省版本的构造函数
构造函数名与类名相同:类名
构造函数可以重载
构造函数可以有任意类型的参数,但没有返回类型
默认构造函数形式:类名::类名(){} //为一个空函数
例:为类Date建立一个构造函数
#include <iostream.h>
class Date {
public:
Date(); // 无参构造函数
Date(int y,int m,int d);
void showDate();
private:
nt 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 *date1;
date1=new Date(1998,4,28); //以上两条语句可合写成:Date *date1=new Date(1998,4,28);
cout<<"Date1 output1:"<<endl;
date1->showDate();
delete date1;
return 0;
}
(2) 利用构造函数创建对象时,通过指针和new来实现。其一般语法形式为:类名 *指针变量 = new 类名[(实参表)];
例:
Date *date1=new Date(1998,4,28); //就创建了对象(*date1)。
构造函数初始化成员有两种方法:
⑴使用构造函数的函数体进行初始化
⑵使用构造函数的初始化列表进行初始化
格式:
funname(参数列表):初始化列表
{ 函数体,可以是空函数体 }
初始化列表的形式:
成员名1(形参名1),成员名2(形参名2),成员名n(形参名n)
例:⑴使用构造函数的函数体进行初始化
class Date
{
int d, m, y;
public:
Date(int dd, int mm, int yy)
{
d=dd;
m=mm;
y=yy;
}
Date(int dd, int mm)
{
d=dd;
m=mm;
}
}
例:
⑵
使用构造函数的初始化列表进行初始化
class Date
{
int d, m, y;
public:
Date(int dd, int mm, int yy):d(dd),m(mm),y(yy)
{ }
Date(int dd, int mm): d(dd),m(mm)
{ }
}
注意:
必须使用参数初始化列表对数据成员进行初始化的几种情况:
1.数据成员为常量
2.数据成员为引用类型
3.数据成员为没有无参构造函数的类的对象
复制构造函数
定义:复制构造函数用一个已有同类对象创建新对象进行数据初始化。
提示:C++为类提供默认版本的复制构造函数,程序员可以定义用户版本的复制构造函数。
语法形式:类名 :: 类名(const 类名 & 引用名 , …);
特点:
⑴复制构造函数名与类名相同,并且也没有返回值类型。
⑵复制构造函数可写在类中,也可以写在类外。
⑶复制构造函数要求有一个类类型的引用参数。
⑷如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。
编译系统自动调用的三种情况:
⑴声明语句中用类的一个已知对象初始化该类的另一个对象时。
⑵当对象作为一个函数实参传递给函数的形参时,需要将实参对象去初始化形参对象时,需要调用复制构造函数。
⑶当对象是函数的返回值时,由于需要生成一个临时对象作为函数返回结果,系统需要将临时对象的值初始化另一个对象,需要调用复制构造函数。
浅复制:
在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制。
即:对于复杂类型的数据成员只复制了存储地址而没有复制存储内容
默认复制构造函数所进行的是简单数据复制,即浅复制
深复制:
通过一个对象初始化另一个对象时,不仅复制了数据成员,也复制了资源的复制方式称为深复制。
自定义复制构造函数所进行的复制是浅复制。
析构函数
定义:析构函数是用于取消对象的成员函数。当一个对象作用域结束时,系统自动调用析构函数
作用:析构函数的作用是进行对象消亡时的清理工作 比如:释放成员(指针)所占有的存储空间,析构函数可以完成上述工作。
没有用户定义析构函数时,系统提供缺省版本的析构函数
析构函数名为: ~ 类名
析构函数没有返回值,不能有参数,也不能重载
定义格式:
类名::~类名()
{
函数语句
}
特点:
⑴析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);
⑵析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;
⑶当撤消对象时,编译系统会自动地调用析构函数。
注:若没有显式定义析构函数,则系统自动生成一个默认形式的析构函数。系统自动生成的默认构造函数形式如下:
类名::~类名(){}
一般情况下,可以不定义析构函数,但如果类的数据成员中包含指针变量是从堆上进行存储空间分配的话,需要在析构函数中进行存储空间的回收。
this指针
this指针的作用:
一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。
例如,调用date.SetMonth(9) <===> SetMonth(&date, 9),this帮助完成了这一转换 .
需要显式引用this指针的三种情况:
⑴在类的非静态成员函数中返回类对象本身或对象的引用的时候,直接使用 return *this,返回本对象的地址时,return this。
⑵当参数与成员变量名相同时,如this->x = x,不能写成x = x。
⑶避免对同一对象进行赋值操作,判断两个对象是否相同时,使用this指针。
例:函数返回对象的引用
#include <iostream>
#include <string>
using namespace std;
class Person{
public:
Person(string n, int a) {
name = n; //这里的 name 等价于this->name
age = a; //这里的 age 等价于this->age
}
int get_age(void) const{ return age; }
Person& add_age(int i) {age += i; return *this; }
private:
string name;
int age;
};
int main()
{
Person Li("Li", 20);
cout<<"Li age = "<< Li.get_age()<<endl;
cout<<"Li add age = "<< Li.add_age(1).get_age()<<end;//增加1岁的同时,可以对新的年龄直接输出;
return 0;
}
类的其他成员
类定义中除了一般指定访问权限的成员,还可以定义各种特殊用途的成员。
比如:常成员、静态成员、友元
常成员:常数据成员是指数据成员在实例化被初始化后,其值不能改变。
在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数
1. 常数据成员
使用const说明的数据成员称为常数据成员。
如果在一个类中说明了常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,而任何其他函数都不能对该成员赋值。
例:
#include<iostream>
using namespace std;
class Mclass
{ public :
int k;
const int M; //说明常数据成员
Mclass() : M(5) { } //用初始式对常数据成员赋值
void testFun()
{ //M++; //错误,不能在成员函数中修改常数据成员
k++; //可以修改一般数据成员
}
} ;
2.常对象
如果在说明对象时用const修饰,则被说明的对象为常对象。
常对象的说明形式如下:
类名 const 对象名[(参数表)];或者const 类名 对象名[(参数表)];
在定义常对象时必须进行初始化,而且不能被更新。
说明:
(1)C++不允许直接或间接更改常对象的数据成员。
(2)C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。
3. 常成员函数
在类中使用关键字const说明的函数为常成员函数,常成员函数的说明格式如下:
类型说明符 函数名(参数表) const;
onst是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。
常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)
静态成员
定义:类成员冠以static声明时,称为静态成员。静态数据成员为同类对象共享。
静态成员函数与静态数据成员协同操作。
静态成员函数:静态成员不属于某一个单独的对象,而是为类的所有对象所共有
静态成员函数的作用:静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员: 保证在不依赖于某个对象的情况下,访问静态数据成员
静态数据成员在定义或说明时前面加关键字static,如:
class A
{
int n;
static int s;
};
对于类的普通数据成员,每一个对象都各自拥有一个副本。(分配不同的存储空间)
对于静态数据成员,每个类只拥有一个副本 。(在静态存储区分配一个存储空间,对所有对象都是可见的)
公有访问权限的静态成员,可以通过下面的形式进行访问
类名::静态成员的名字
对象名.静态成员名字
对象指针->静态成员的名字
在静态成员函数内部,直接访问。
静态数据成员声明及初始化:
在类外进行静态数据成员的声明
类型 类名::静态数据成员[=初始化值]; //必须进行声明
不能在成员初始化列表中进行初始化
如果未进行初始化,则编译器自动赋初值(默认值是0)
初始化时不能使用访问权限
例:
#include<iostream>
using namespace std ;
class counter
{ static int num ;
public :
void setnum ( int i ) { num = i ; }
void shownum() { cout << num << '\t' ; }
} ;
int counter :: num = 0 ;
int main ()
{ counter a , b ;
a.shownum() ; b.shownum() ;
a.setnum(10) ;
a.shownum() ; b.shownum() ;
cout<<endl ;
}
静态函数
说明:
⑴静态成员函数在类外定义时不用static前缀。
⑵静态成员函数主要用来访问同一类中的静态数据成员。
⑶私有静态成员函数不能在类外部或用对象访问。
⑷可以在建立对象之前处理静态数据成员。
⑸编译系统将静态成员函数限定为内部连接(在其他文件中不可见)。
⑹静态成员函数中是没有this指针的。
⑺静态成员函数不访问类中的非静态数据成员。如有需要,只能通过对象名(或指向对象的指针)访问该对象的非静态成员。
友元:不建议使用!
二.心得体会
这一章学习了类与对象,可以说 是一个新的开始,是从C语言到C++的开始!这是新开始的一章,也是最难的一章,也是最重要的一章!这一章令我印象最深刻的就是各种铺天盖地的定义,定义中的定义,定义中的定义中的定义,这也是最让我头疼的一部分,经常的记混,就好像知道这个人长什么样却怎么都对不起他的人名来...学习完这一章我们可以开始真正的写一些程序了,在写程序的过程中我也体会到了很多:就是思考要细致,写一个程序要考虑到各种数据使用到各种类,时间类、日期类、成绩类······这首先是对自己细心程度的一个考验。也发现了自己的一些问题,就像对函数的调用和运用十分的生疏,跟原来的课程不扎实有着极其大的关系,需要自己在回顾前面的内容,不能狗熊掰棒子。最后最大的收获还是改掉了一口气测试一个整程序的毛病,开始了滚雪球法,当面对一个庞大的程序时,你不一点一点测试好每个类是否正确的话,到了后面真的是积累成一个巨大的困难...整体来说自己对这一章的认知还是很肤浅,不能听懂每个知识点,还是要跟老师说的一样,花更多的时候去悟,去慢慢的啃。