目录
一、类和对象的介绍
类就是包含了一系列属性和行为的总和,而由类构成的,拥有类里面这一系列属性和行为的就是对象。
例如:
#include<iostream>
using namespace std;
class Point
{
private:
int xp;
int yp;
public:
void setX(int x)
{
xp = x;
}
void setY(int y)
{
yp = y;
}
void show(void)
{
cout << xp <<" "<< yp;
}
};
int main()
{
Point a; //创建一个Point类的对象,叫做a
return 0;
}
定义了一个叫做Point的类,它所含有的属性有两个坐标值, 行为有若干个函数。
二、类设计
1、类的定义
格式:
类的定义
class 类名
{
private:
私有成员(数据和函数)
protected:
保护成员(数据和函数)
public:
公共成员(数据和函数)
};
2、成员类型
public:所有的成员属性和成员函数可以在类内访问,也可以在外部通过创建对象进行调用
private:所有的成员属性和成员函数可以在类内访问,不能在外部访问,一般设计接口函数 get set函数
protected:所有的成员属性和成员函数可以在类内访问,也可以在它的子类中访问
三、成员函数
成员函数分为:普通成员函数和特殊成员函数,特殊成员函数里有:构造函数、析构函数和复制构造函数。
成员函数可以放在类内,也可以放在类外,在类内定义默认是内联函数,在类外定义需要在类内声明,并且定义的时候函数名前要加 类名::
1、this 指针
成员函数内部有一个this指针。谁调用成员函数,this指针就指向谁。类里面所有的对象都有独立的属性成员,成员函数是共有的。this指针是类成员函数内部隐式存在的。如果数据成员的名称和参数的名称重复了,就需要显式使用this指针赋值。
//声明一个Point类的成员函数setX,Point内有叫xp的成员变量,和函数形参同名
void Point::setX(int xp)
{
this->xp = xp; // 指针形式
// this指针指向了调用它的对象 ,this指针保存了对象的地址
// *this --- 调用的对象
(*this).xp = xp; // 对象形式
}
2、普通构造函数
作用:构造函数是在创建对象的时候自动调用,一般完成对象的赋值或者初始化操作。
普通构造函数格式:
类名(形参)
{
}
注意:1、构造函数的名称一定要和类名一致
2、不写返回值类型
3、创建对象的时候自动调用,不能手动调用
4、构造函数写在public权限里面。
5、构造函数可以有多个,支持函数重载
6、构造函数如果自定义就用定义的构造函数,如果没有定义就使用系统默认。
7、构造函数支持 形参默认值,但是形参默认是和函数重载在一起的时候小心冲突。
8、构造函数写在类外部,有默认参数的时候,默认参数只能有一个位置,定义或者声明,尽量将默认参数写在类内部的函数声明的位置。
#include<iostream>
using namespace std;
class Point
{
private:
int xp;
int yp;
public:
Point(int x=0,int y=0) //带形参和默认值的构造函数
{
xp = x;
yp = y;
cout << "有参构造函数执行" << endl;
}
void show(void)
{
cout << xp << "," << yp << endl;
}
};
int main()
{
Point a(1,3);
a.show();
Point b;
b.show();
Point c(3);
c.show();
return 0;
}
3、析构函数
作用:对象销毁的时候自动调用的函数,一般用来清除数据成员的空间,和构造函数相反。
格式:
~类名()
{
}
注意:1、析构函数只能有一个
2、使用对象超出其作用域 ,自动调用对应的类的析构函数
3、析构函数是用来清理空间(有堆区空间,则需要清理堆区空间)(不是释放成员空间),防止存在内存泄漏,产生隐患
4、定义时函数名与类名相同,并且前面加上~
6、没有返回值类型,没有形参
7、析构函数可以显式调用,通常不显式调用
8、析构如果自定义就使用定义的析构,如果没有提供那就调用系统提供的析构函数
#include<iostream>
using namespace std;
class Point
{
private:
int xp;
int yp;
int *zp;
public:
// 创造对象的时候自动调用
Point(int x,int y)
{
xp = x;
yp = y;
zp = new int;
cout << "调用有参构造函数" << endl;
}
// 对象销毁的自动调用,析构函数
~Point()
{
if(zp != NULL)
{
delete zp;
zp = NULL;
cout << "析构函数" << endl;
}
}
};
int main()
{
Point a(1,3);
a.~Point();
return 0;
}
4、复制构造函数
作用:对象复制,可以实现将一个对象中的数值一对一的复制到另一个空间里面去。
格式:
类名(const 类名 &形参名)
{
// 自己实现对成员数据的一一赋值
}
注意:1、复制构造函数和构造函数一样,有默认存在的,但是如果数据成员里面有指针,那就会造成多个对象的成员指向了同一片空间,就可能造成误操作。所以需要自己构建一个复制构造函数。
2、一个类里面复制构造函数只能有一个,和类同名,有一个参数,一般是常量引用对象。
3、一般复制构造不写,使用默认的,但是如果成员有指针或者堆区空间就要自定义。
4、普通构造函数和复制构造函数只会执行一个,具体执行哪个看形参。
#include<iostream>
using namespace std;
class Point
{
public:
int xp;
int yp;
int *zp;
Point(int x,int y)
{
xp = x;
yp = y;
zp = new int;
cout << "普通构造函数" << endl;
}
Point(const Point& p1)
{
xp = p1.xp;
yp = p1.yp;
zp = new int;
// 拷贝堆区的值
*zp = *(p1.zp);
cout << "复制构造函数" << endl;
}
~Point()
{
if(zp != NULL)
{
delete zp;
zp = NULL;
cout << "析构函数" << endl;
}
}
void show(void)
{
cout << "xp:" << xp << ",yp:" << yp << ",*zp:" << *zp << endl;
}
};
int main()
{
Point a(1,3); // 调用普通构造函数
*(a.zp) = 10;
a.show();
Point b(a); // 调用复制构造函数
*(b.zp) = 20;
b.show();
return 0;
}
5、数据成员的初始化
格式:
类名(形参):成员名(参数名),成员名(参数名)
{
}
注意:1、初始化表达式放在构造函数后面,记得加 “:”号。
2、如果成员里面有const成员那么只能通过初始化的形式赋值
3、如果构造函数重载了,在所有的构造函数后面都要设置初始化表达式
#include<iostream>
using namespace std;
class Point
{
public:
int xp;
int yp;
const int zp;
Point(int x,int y,int z):xp(x),yp(y),zp(z) //初始化的格式
{
}
Point(int z):zp(z) //初始化的格式
{
xp = 5;
yp = 10;
}
void show(void)
{
cout << "xp:" << xp << ",yp:" << yp << ",zp" << zp << endl;
}
};
int main()
{
Point a(1,3,5);
a.show();
Point b(5);
b.show();
return 0;
}
四、类的特殊成员
1、const数据成员
数据成员前面用const限定,成员限定为只读。
1、赋值用初始化表达式的方式进行赋值
2、如果构造函数重载,所有的构造函数对于const成员都要进行初始化
3、const成员如果直接使用=赋值,那就可以不用初始化表达式
#include<iostream>
using namespace std;
class Point
{
private:
int xp;
const int yp = 100;
const int zp;
public:
Point(int x,int z):xp(x),zp(z) //初始化
{
// zp = z; // 在这里写是相当于对zp进行赋值,是错误的写法
}
void show(void)
{
cout << "xp:" << xp << ",yp:" << yp << ",zp:" << zp << endl;
}
};
int main()
{
Point a(1,3);
a.show();
return 0;
}
2、static数据成员
数据成员用static限定,成员空间创建在静态区,程序加载的时候空间就分好了。
static数据成员在类中所有对象共享,在类外使用:数据类型 类名::静态成员名 = 值;
static数据成员不能使用this进行操作
#include<iostream>
using namespace std;
class Point
{
private:
int xp;
int yp;
static int zp;
public:
Point(int x,int y):xp(x),yp(y){
}
void show(void)
{
cout << "xp:" << xp << ",yp:" << yp << ",zp:" << zp << endl;
}
};
int Point::zp = 100;
int main()
{
Point a(1,2);
a.show();
Point b(3,4);
b.show();
return 0;
}
3、引用成员
给变量起别名,一般用在传参,引用定义的时候必须初始化
#include<iostream>
using namespace std;
class Point
{
private:
int xp;
int yp;
int &zp; //定义引用成员
public:
Point(int x,int y,int &z):xp(x),yp(y),zp(z)
{
// xp = x;
// yp = y;
// zp = z; // 错误的,因为在构造函数{}内属于赋值不属于初始化
}
void show(void)
{
cout << "xp:" << xp << ",yp:" << yp << ",zp:" << zp << endl;
}
};
int main()
{
int b = 30;
Point a(1,3,b);
a.show();
return 0;
}
4、const成员函数
格式:
数据类型 函数名(形参) const
{
}
const成员函数函数只能对成员进行读操作,不能对成员进行写操作。在这个函数内部只能调用其他const成员函数,不能调用非const成员函数。
#include<iostream>
using namespace std;
class Point
{
private:
int xp;
int yp;
public:
Point(int x,int y):xp(x),yp(y){
}
void test1(void)
{
cout << "无cosnt函数" << endl;
}
void test2(void) const
{
cout << "有const的函数 " << endl;
}
void show(void) const
{
// test1(); //调用非const函数会报错
test2();
cout << "xp:" << xp << ",yp:" << yp << endl;
}
};
int main()
{
Point a(3,4);
a.show();
return 0;
}
5、static成员函数
static成员的函数:属于类内,可以使用对象进行调用,也可以使用类直接调用,调用格式:类名::函数名(实参列表);
1、静态成员函数内只能调用静态成员函数
2、静态成员函数中只能访问静态数据,不能访问普通数据所以静态成员函数就是为了访问静态成员设计的。
#include<iostream>
using namespace std;
class Point
{
private:
int xp;
int yp;
static int zp;
public:
Point(int x,int y):xp(x),yp(y){
}
void test1(void)
{
cout << "无static函数" << endl;
}
static void test2(void)
{
cout << "有static的函数 " << endl;
}
static void show(void)
{
test2();
cout << "zp:" << zp << endl;
zp = 800;
cout << "zp:" << zp << endl;
}
};
int Point::zp = 500;
int main()
{
Point a(3,4);
a.show();
Point::show();
return 0;
}
6、其他对象做成员
类内的成员可以是别的类构成的对象,例如:
#include<iostream>
using namespace std;
class Point
{
public:
int xp;
int yp;
public:
Point(int x,int y):xp(x),yp(y){}
};
class Line
{
public:
Point xl;
Point yl;
public:
Line(Point x,Point y):xl(x),yl(y){}
};
class Surface
{
public:
Line xs;
Line ys;
Line zs;
public:
Surface(Line x,Line y,Line z):xs(x),ys(y),zs(z){}
void show(void)
{
cout << xs.xl.xp <<" "<< ys.xl.xp <<" "<< zs.xl.xp <<endl;
}
};
int main()
{
Point a(10,10);
Point b(20,20);
Point c(30,30);
Point d(40,40);
Point f(50,50);
Point g(60,60);
Line l_1(a,b);
Line l_2(c,d);
Line l_3(f,g);
Surface s_1(l_1,l_2,l_3);
s_1.show();
return 0;
}
五、友元函数
友元函数的作用就是实现用外部函数去访问类内的private成员。
函数格式:
返回值类型 函数名(const 类名 &引用名)
{
}
定义完函数后,还需要在类内声明这个函数是类的友元函数。
声明格式:
friend 返回值类型 函数名(const 类名 &引用名);
#include<iostream>
using namespace std;
class Point
{
private:
int xp;
int yp;
public:
Point(int x,int y):xp(x),yp(y){
}
// 声明printP是Point的朋友
friend void printP(const Point &p1);
};
// 定义一个外部函数想要访问类对象里面的私有成员xp;
void printP(const Point &p1)
{
cout << p1.xp << endl;
}
int main()
{
Point a(1,3);
printP(a);
return 0;
}
友元除了这样子使用,还可以用一个类的成员函数定义为另一个类的友元,也可以定义友元类。本文不涉及。