基础知识点
●类的定义
类是对具有相同属性和行为的一组对象的抽象与统一描述。是用户自定义的数据类型。类的定义包括行为和属性两个部分。属性以数据表示,行为通过函数实现。
类的成员可以是其他类的对象,但不能以类自身的对象作为本类的成员,而类自身的指针和引用可以作为类的成员。类定义必须以分号“;”结束。类与结构体的区别:没有明确指定类成员的访问权限时,C++结构体的成员是公有的,而类的成员是私有的。
●访问控制
类的访问属性有:
Public 公有成员 类的外部接口
Protected 保护成员 仅允许本类成员函数及派生类成员函数访问
Private 私有成员 仅允许本类成员函数访问
●成员函数
类的成员函数是实现类的行为属性的成员。一般将成员函数声明为函数原型,在类外具体实现成员函数
●对象
对象是类的实例或实体。类与对象的关系,如同C++基本数据类型和该类型的变量之间的关系。
对象的定义格式如下:
类名 对象名1,对象名2,…,对象名n;
必须在定义了类之后,才可以定义类的对象
●类定义和使用时应注意:
在类的定义中不能对数据成员进行初始化
类的任何成员都必须指定访问属性,一般将数据成员定义为私有成员或保护成员,将成员函数定义为公有成员
类中的数据成员可以是C++语法规定的任意数据类型
类的成员可以是其他类的对象,称为类的组合。但不能以类自身的对象作为本类的成员
类定义必须以分号“;”结束
class与struct的不同:
class中,成员缺省情况是private
struct中,成员缺省情况是public
●构造函数
构造函数是用于创建对象的特殊成员函数。当创建对象时,系统自动调用构造函数
构造函数的作用是:
为对象分配空间;对数据成员赋初值;请求其他资源
没有用户定义的构造函数时,系统提供缺省版本的构造函数
构造函数名与类名相同:类名
构造函数可以重载。构造函数可以有任意类型的参数,但没有返回类型
●析构函数
析构函数是用于取消对象的成员函数
当一个对象作用域结束时,系统自动调用析构函数
析构函数的作用是进行对象消亡时的清理工作
没有用户定义析构函数时,系统提供缺省版本的析构函数
析构函数名为: ~ 类名
析构函数没有参数,也没有返回类型。
对象生存期结束时,需要做清理工作,比如:释放成员(指针)所占有的存储空间
析构函数可以完成上述工作。
析构函数自动调用(隐式调用)
析构函数没有返回值,不能有参数,也不能重载
定义格式如下(类外实现):
类名::~类名()
{
函数语句
}
析构函数有以下一些特点:
① 析构函数与构造函数名字相同,但它前面必须加一个波浪号(~);
② 析构函数没有参数,也没有返回值,而且不能重载。因此在一个类中只能有一个析构函数;
③ 当撤消对象时,编译系统会自动地调用析构函数。
●默认构造函数
如果类中没有定义构造函数,系统将自动生成一个默认形式的构造函数,用于创建对象,默认构造函数形式:
类名::类名(){}
默认构造函数是一个空函数。
一般情况下,可以不定义析构函数
但如果类的数据成员中包含指针变量是从堆上进行存储空间分配的话,需要在析构函数中进行存储空间的回收
●通常,利用构造函数创建对象有以下两种方法:
(1) 利用构造函数直接创建对象.其一般形式为:
类名 对象名[(实参表)];
这里的“类名”与构造函数名相同,“实参表”是为构造函数提供的实际参数
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)。
●数据成员的初始化
构造函数初始化成员有两种方法
1.使用构造函数的函数体进行初始化
使用构造函数的初始化列表进行初始化
●必须使用参数初始化列表对数据成员进行初始化的几种情况
1.数据成员为常量
2.数据成员为引用类型
3.数据成员为没有无参构造函数的类的对象
●类成员的初始化的顺序
按照数据成员在类中的声明顺序进行初始化,与初始化成员列表中出现的顺序无关
●复制构造函数
复制构造函数用一个已有同类对象创建新对象进行数据初始化
C++为类提供默认版本的复制构造函数
程序员可以定义用户版本的复制构造函数
语法形式
类名 :: 类名(const 类名 & 引用名 , …);
复制构造函数名与类名相同,并且也没有返回值类型。
复制构造函数可写在类中,也可以写在类外。
复制构造函数要求有一个类类型的引用参数。
如果没有显式定义复制构造函数,系统自动生成一个默认形式的复制构造函数。
以下三种情况下由编译系统自动调用:
1.声明语句中用类的一个已知对象初始化该类的另一个对象时。
2.当对象作为一个函数实参传递给函数的形参时,需要将实参对象去初始化形参对象时,需要调用复制构造函数。
3.当对象是函数的返回值时,由于需要生成一个临时对象作为函数返回结果,系统需要将临时对象的值初始化另一个对象,需要调用复制构造函数。
例子
●C++类定义的格式:
class 类名
{
public:
公有数据成员和成员函数;
protected:
保护数据成员和成员函数;
private:
私有数据成员和成员函数;
};
●类定义举例
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;
}; //类定义以分号结束
●成员函数的定义
返回值类型 类名::成员函数名(参数表)
{
函数体
}
●为类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;
}
●使用构造函数的函数体进行初始化
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;
}
}
●使用构造函数的初始化列表进行初始化
格式:
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)
{ }
}
●必须使用参数初始化列表对数据成员进行初始化的几种情况
#include <iostream>
using namespace std;
class A{
public:
A(int i):x(i),rx(x),pi(3.14)
{}
void display()
{cout<<"x="<<x<<"rx="<<rx<<"pi="<<pi<<endl;}
private:
int x,℞
const float pi;
};
int main(){
A aa(10);
aa.display();
return 0;
}
数据成员为常量
数据成员为引用类型
#include<iostream>
using namespace std ;
class A
{ public :
A ( int x ) : a ( x ) { }
int a ;
} ;
class B
{ public :
B( int x, int y ) : aa( x ), b( y ) { }
void out()
{ cout << "aa = " << aa.a << endl << "b = " << b << endl ; }
private :
int b ;
A aa ;
} ;
int main ()
{ B objB ( 3, 5 ) ;
objB . out ( ) ;
}
数据成员为没有无参构造函数的类的对象
●添加一个或者多个同学基本信息
void student_list::add()
{
int no,x,y,z;
string name;
system("cls");
cout<<" 按学号、姓名、数学、英语、c++顺序输入学生信息,学号输-1表示结束"<<endl;
while((cin>>no)&&no!=-1)
{
cin>>name>>x>>y>>z;
student s(no,name,x,y,z);
list[n++]=s;
sortList();
}
}
●对改变了的成绩表进行名次更新
void student_list::sortList()
{
sort(list,list+n,cmp1);
int i;
for(i=0;i<n;i++)
list[i].setOrder(i+1);
}
void student_list::display(int flag)//flag=1,按学号排列; flag=0,按名次排列
{
if(flag) sort(list,list+n,cmp2);
else sort(list,list+n,cmp1);
cout<<"姓名"<<"\t"<<"学号"<<"\t"<<"数学"<<"\t"<<"英语"<<"\t"<<"c++"<<"\t"<<"平均成绩"<<"\t"<<""<<endl;
for(int i=0;i<n;i++)
list[i].display();
}
int student_list::search(int no)
{
int i;
for(int i=0;i<n;i++)
if(list[i].getNo()==no)
return i;
return -1;
}
void student_list::query()
{
int no,i;
system("cls");
cout<<"请输入要查询同学的学号,按-1结束查询:";
while(cin>>no&&no!=-1)
{
i=search(no);
if(i!=-1)
{
cout<<"姓名"<<"\t"<<"学号"<<"\t"<<"数学"<<"\t"<<"英语"<<"\t"<<"c++"<<"\t"<<"平均成绩"<<"\t"<<""<<endl;
list[i].display();
cout<<"请输入要查询的同学的学号,按-1结束查询:";
}
else cout<<"学号输入有误,请重输,输入-1结束查询"<<endl;
}
}
●实现按学号进行修改操作,并输出查找的结果
void student_list::change()
{
}
void student_list::deleteStu()
{
}
int main()
{
student s(1,"ff",66,77,88);
s.display();
cout<<s.getAverage()<<endl;
s.setName("方法");
s.display();
student_list c;
c.add();
c.display(1);
c.query();
c.display(0);
return 0;
}
学习感悟
初学c++的时候代码相对来说比较简单,就算有些地方遇到了问题也可以很快解决。但是自从学了第六章以后,就觉得这门课程突然变难了很多。主要原因是因为很多复杂问题都可以通过不同的类来组合解决,因此程序复杂程度也变高了,就拿学生信息系统这个程序来说,一个程序写下来就需要两百行左右,从头到尾都下来都感到很吃力,更不用提自己设计了。复杂的程序需要考虑的细节更多,自然更容易出错,一行行代码看的眼花缭乱,问题一堆上来就更加烦躁,很难静下心来找出问题所在。解决这些问题还需要下一番功夫,等自己调的程序多了,积累更多的经验以后,或许再回头这些问题就会变得简单很多,不会再感到很复杂了。
随着c++学习的深入,调试程序的方法也要改进。不能再像从前一样打完整个程序之后再进行调试。现阶段写的程序往往需要包括许多个不同的类,如果前面的类出现问题,就会造成输出结果出现错误,错误的数据像下一个类传递,以至于到最后很难找出哪里出现了问题。因此在写代码的时候,每完成一个类就要进行调试,从而确保它的正确性。