一. 类和对象的定义与访问
类是用户定义的数据和操作这些数据的函数的封装。
二. 定义类和对象
1.“类”里面包含的是一些性质和功能相同或相似的东西,它是抽象的,当属性赋给具体的值,方法有具体的内容时,才成为对象。
2.C++中,数据的存储结构,称为数据成员;方法用函数实现,称为成员函数。
3.语法规则:
Class<类名>
{
public:
公有段数据成员和函数;
protected:
保护段数据成员和成员函数;
private:
私有段数据成员和成员函数;
};分号千万不能忘记!!!
4.私有成员(private)只能在类中可见,如果私有成员放在第一行,可以省略private;保护成员(protected)在类和它的派生类中可见;公有成员在类和类外均可见。一般数据成员私有化,成员函数公有化。
5.成员函数的执行语句如果简单,可以在定义时直接写出。如果执行语句比较麻烦,就需要在类外进行说明,说明语法形式为:
返回类型 类名::函数名(参数表)
{执行语句}
例如:
class Grade
{
int chinese,math,English;
public:
void setGrade(int yw,int sx,int yy);
};
void Grade::setGrade(int yw,int sx,int yy)
{
chinese=yw;
math=sx;
English=yy;
}
- 如果::前面没有类名,这个成员就不是成员函数,而是全局的普通函数。
6.公有成员可以访问和修改数据成员。
另外,也可以用数组组织对象。
例如:Student students[100];
对其内容的操作形式为student[i].birthday.setDate(1982,6,6);
tips:
- 每个类可以没有成员,也可以有多个成员。
- 所有成员必须在类的内部声明,一旦类定义完成后,就没有任何其他方式可以再增加成员了。
- 类定义一般放在程序文件开头,或者放到头文件中被程序文件包含。也可以放到函数内部或局部作用域中,此时这个定义是局部的。
例如:
class Data
{//全局的data类定义
void show();//成员函数原型声明
int data;//数据成员
};//结束定义
void fan
{//全局data在fun函数中无效,有效的是局部定义的。
class Data{//局部的data类定义
void show() {cout<<data;}//set函数定义
int data;//数据成员
}//定义结束
};
- 类型声明时并不会产生该成员的实体,即为它分配存储空间。
三. 访问对象成员
1. 公有成员是类与外面的接口,运算符“.”和“->”用于访问对象成员。
例如:
(1)访问对象的公有成员
class Grade
{
public:
int x,y;
void print()
{
cout<<x<<“,”<<y;
}
};
int main()
{
Grade fs;
fs.x=100;
fs.y=88;
fs.print();
}
(2)用指针访问对象成员
#include<iostream>
using namespace std;
class Tclass
{
public:
int x,y;
void print() {cout<<x<<","<<y<<endl;}
};
int add(Tclass*ptf)
{
return (ptf->x+ptf->y);
}
int main()
{
Tclass test,*pt=&test;
ptf->x=100;
ptf->y=200;
pt->print();
test.x=150;
test.y=450;
test.print();
cout<<"x+y="<<add(&test)<<endl;
}
2.内联函数
- 类的成员函数可以指定为内联函数。默认情况下,在类体中定义的成员函数如果不包括循环等控制结构,符合内联函数要求时,C++会自动将它们作为内联函数处理。
- 也可以显式声明,如:
class Data
{
int getx() {return x;} //内联成员函数
inline int gety() {return y;} //显式指定内联成员函数
inline void setxy(int _x,int_y);
void display();
int x,y;
};
inline void Data::setxy(int _x,int _y) //内联成员函数
{
x=_x;y=_y;
}
void Data::display() //非内联成员函数
{
.....(函数体)
}
三. this指针
1.this指针并不是对象本身的一部分,它是当在类的非静态成员函数中访问类的费静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数,或者在类的非静态成员函数中返回类对象本身或对象的引用时,直接使用return *this,返回本对象的地址时,return this,再或者参数与成员变量名相同时,写成this->x=x。
2.this指针的显式使用主要是在运算符重载,自引用等场合。
3.this指针是一个常指针,一旦初始化,就不能再对其进行修改和赋值。
四.构造函数和析构函数
1.构造函数就是用来用在创建对象时初始化对象,为对象数据成员赋初始值。
2.类的数据成员是不能在类定义时初始化的,因为定义时并没有占用存储空间。例如:
class point
{
int x=1,y=6; //错误,不能在类定义中对数据成员初始化
.... //其他成员
}
如果一个类中所有的数据成员市公有的,就可以在定义对象时对数据成员进行初始化,如:
class point
{
public:
int x,y;
...(其他成员)
};
Point one={10,10}; //对象初始化
Point A[3]={{(10,10),(20,20),(30,30)}};
但如果数据成员是私有的,这种方法就不可行。
3.构造函数可以有任何类型的参数,但不能有返回类型。语法规则如下
类名::类名(参数表);
4.构造函数初始化列表
类名(形式参数列表):构造函数初始化列表
{ 函数体 }
#include<iostream>
using namespace std;
class Cuboid
{
public:
Cuboid(int l,int h,int d); //构造函数
int volumn(){return lenth*height};
private:
int lenth,height,depth;
};
Cuboid::Cuboid(int l,int h,int d):lenth(l),height(h),depth(d) //带构造函数初始化列表的构造函数
{
cout<<"Cuboid:"<<"L="<<l<<",H="<<h<<",D="<<d<<endl;
}
5.构造函数的重载
- 构造函数允许被重载,只要每个构造函数的形参列表是唯一的。不同的构造函数允许用不同的方式来初始化数据成员。
#include<iostream>
using namespace std;
class Point
{
public:
Point() {x=y=0;} //无参的构造函数
Point(int a,int b):x(a),y(b){} //有参的构造函数
~Point()
{cout<<"析构函数被调用"<<endl;}
void display() {cout<<"x="<<x<<",y="<<y<<endl;}
private:
int x,y;};int main(){ Point m; m.display(); Point n(1,2); n.display(); return 0;}
- 析构函数
析构函数是在类名之前冠以一个波浪号~。析构函数没有参数也没有返回类型。析构函数在类对象作用域结束时自动调用。
类名::~类名();
何时调用析构函数:
- 对象在程序运行超出其作用域时自动撤销,撤销时自动调用该对象的析构函数。
- 如果new动态运算地建立了一个对象,那么用delete运算释放该对象时,调用该对象的析构函数。
例子见上一个程序的红色字
五.带参的构造函数
我们在写程序的时候,一般要定义两个构造函数,包括不带参的构造函数和缺省的构造函数。描述信息的类叫做数据类,重点描述信息,有一组get,set函数。
六.复制构造函数
1.复制构造函数的作用就是用一个已经生成的对象来初始化另一个同类的对象。例如:
Point pt1(10,20); Point pt2=pt1;
2.语法形式
类名::类名(const类名&引用名,...)
{函数体}
为了保证引用对象不被修改,通常把引用参数说明为const参数,例如:
class Point
{
public:
Point():x(2),y(3){ } //默认构造函数
Point(const Point& r):x(r,x),y(r,y){} //复制构造函数
Point(int a,int b):x(a),y(b){ } //带参构造函数
private:
int x,y;
};
3.调用复制构造函数的时机
A.用一个对象显式或阴式初始化另一个对象。
- C++支持两种初始化形式:复制初始化和直接初始化。复制初始化使用等号=,而直接初始化将初始化式放在圆括号中。
- 直接初始化会调用与实参匹配的构造函数;而复制初始化总是调用复制构造函数。
Point pt1(10,20);
Point pt2=pt1; //复制初始化
Point pt3(pt1); //直接初始化
B.函数参数按值传递对象时或函数返回对象时。
- 当函数形参为对象类型,而非指针和引用类型时,函数调用按值传递对象,即编译器调用复制构造函数产生一个实参对象副本传递到函数中。
C.根据元素初始化列表初始化数组元素时。
- 如果没有为类类型数组提供元素初始化式,则将用默认构造函数初始化每个元素。如果使用常规的大括号的数组初值列表形式来初始化数组时,则使用复制初始化来初始化每个元素。
- 总的来说,正是有了复制构造函数,函数才可以传递对象和返回对象,对象数组才能用初始列表的形式初始化。
4.深复制和浅复制
- 如果一个拥有资源(如用new得到的动态内存)的类对象发生复制的时候若对象数据与资源内容一起复制,称为深复制。在用一个对象初始化另一个对象时,只复制了数据成员,而没有复制资源,使两个对象同时指向了同一资源的复制方式称为浅复制。默认复制构造函数所进行的是简单数据复制,即浅复制 。
- 深复制构造函数必须显式定义,其语法形式为类名::类名([const] 类名 &对象名);
- 对复杂类型的成员变量,使用new操作符进行空间的申请,然后进行相关的复制操作。
#include<iostream>
#include<string.h>
using namespace std;
class CA
{
public:
CA(int b,char *cstr) //构造函数
{
a=b; str=new char[b];
strcpy(str,cstr);
}
CA(const CA&C) //复制构造函数
{
a=C.a;str=new char[a]; //深复制,浅复制则写成str=C.str;
if(str!=0) strcpy(str,C.str);
}
void show()
{
cout<<str<<endl;
}
~CA() //析构函数
{
delete str;
}
private:
int a;char *str;
};
int main()
{
CA a(10,"hello");
CA b=a;
b.show();
return 0;
}
七.类的其他成员
1.常成员
常数据成员是指数据成员在实例化被初始化后,其值不能改变。
2.常成员函数
在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数。只能通过初始化列表对该数据成员进行初始化,而任何其他函数都不能对该成员赋值。
- 在说明对象时用const修饰的对象为常对象。
在定义常对象时必须进行初始化,而且不能被更新。
C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。
#include<iostream>
using namespace std;
class T_class
{
public:
int a, b;
T_class( int i, int j )
{
a=i; b=j;
}
} ;
int main()
{
const T_class t1( 1, 2 );
T_class t2( 3, 4 );
//t1.a=5;
//t1.b=6;
t2.b=8;
t2.a=7;
cout<<"t1.a="<<t1.a<<'\t'<<"t1.b="<<t1.b<<endl;
cout<<"t2.a="<<t2.a<<'\t'<<"t2.b="<<t2.b<<endl;
}
七.静态成员
- 类成员冠以static声明时,称为静态成员。 静态数据成员为同类对象共享。静态成员函数与静态数据成员协同操作。
- 静态成员不属于某一个单独的对象,而是为类的所有对象所共有。
静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员: 保证在不依赖于某个对象的情况下,访问静态数据成员。 - 静态数据成员在定义或说明时前面加关键字static。
- 公有访问权限的静态成员,可以通过下面的形式进行访问:
类名::静态成员的名字
对象名.静态成员名字
对象指针->静态成员的名字
在静态成员函数内部,直接访问。 - 在类外必须进行静态数据成员的声明类型 类名::静态数据成员[=初始化值]; 不能在成员初始化列表中进行初始化。
八.静态成员函数
静态函数仅可以访问静态成员,或是静态成员函数或是静态数据成员。
静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
- 定义静态成员函数的格式如下:
调用公有静态成员函数的一般格式有如下几种:
类名::静态成员函数名(实参表)
对象. 静态成员函数名(实参表)
对象指针->静态成员函数名(实参表)
class X
{ public :
static void StaFun ( int i , X *ptr ) ;
int staDat ;
} ;
void X :: StaFun ( int i , X * ptr )
{
stadat=i; //错误,不知引自哪个成员
}
九.友元函数
如果在本类以外的其他地方定义了一个函数
这个函数可以是不属于任何类的非成员函数,
在类体中用friend对其进行声明,此函数就称为本类的友元函数。他可以可以访问这个类中的私有成员。
#include<iostream>
#include<cmath>
using namespace std;
class Point
{
public:
Point(int _x=0,int _y=0):x(_X),y(_y) {}
private:
int x,y;
friend double distance(Point& r,Point& r2);
};
double distance(Point& r1,Point& r2)
{
double x=r2.x>r1.x? r2.x-r1.x:r1.x-r2.x;
double y=r2.y>r1.y? r2.y-r1.y:r1.y-r2.y;
return sqrt(x*x+y*y);
}
int main()
{
Point a(1,1),b(5,5);
cout<<distance(a,b);
return 0;
}
- 当一个类中含有已经定义的类类型成员,带参数的构造函数对数据成员初始化,须使用初始化语法形式。
构造函数 ( 形参表 ) : 对象成员1(形参表 ) , … , 对象成员n (形参表 ) ;
成员对象的构造函数调用次序和成员对象在类中的说明次序一致。
十.学习心得
从这一章开始,我们学习的东西就有很大的转变,最根本的还是解题思想不一样了,所以变化有点大,刚开始不明白类到底是个什么东西,所以理解的不好,知识点就像老师说的一样,滚雪球一样越来越大,所以不会的越来越多,按理来讲课程笔记要自己写,不能抄课件抄课本什么的,但是实话来讲,这一章的东西有点杂乱,而且我学习的不是很好,自己心里没有知识的结构体系,所以写不出什么东西来,虽然我知道它就像纸老虎,但我还是不能在很短的时间内攻克它,所以还请老师原谅这一次有很多东西我是看着课本和课件来的,有的时候就是抄一遍,但是我觉得抄一遍最起码可以帮助理清思路,就算不能理清思路,至少可以记住点东西,不要让自己学了半天还是很懵,关于类我还是不太会写,所以我打算充分利用好这个五一假期,在完成老师布置作业的基础上,多复习复习前面的内容,争取成为以后学习越来越容易的那个而不是越来越难最后放弃自我的那个,加油!