一,定义

类的基本思想是数据抽象封装。数据抽象是一种把接口和实现分离的编程技术。类的接口包括用户所能够执行的操作,类的实现包括类的数据成员、负责接口实现的函数体和各种私有函数。

封装实现了类的接口和实现的分离。封装隐藏了类的实现,封装过后,用户只能访问类的接口,而不能访问类的实现。

1.类的声明

以数据成员的方式描述数据部分,以成员函数(被称为方法)的方式描述接口函数。

class 类名称

{

public:

公有成员(外部接口)

private:

私有成员 (只允许本类中的函数访问,而类外部的任何函数都不能访问)

protected:

保护成员(与private类似,差别表现在继承与派生时)

};

2.类方法定义:

描述如何实现类成员函数。

 简单地说,类声明给出了类的框架,而方法定义给出了类的细节。

 

3.类与结构体的区别

class数据成员默认私有

struct数据成员默认公有

 

4.数据成员

类中有两个关键字privatepublic,它们描述了程序对类的成员的访问控制。由于隐藏数据是OOP的主要目的之一,所以数据成员一般放在private部分,而接口函数则放在public部分。

 类可以没有成员,也可以定义多个成员。成员可以是数据、函数或类型别名。所有的成员都必须在类的内部声明。
没有成员的类是空类,空类也占用空间。

注:类的成员默认是私有的,但是为了严谨一般还是用private关键字注明,而结构体的成员默认是公有的。

 

5.成员函数

(1)在类外定义一个成员函数时,需要使用域解析运算符(::)来标识函数所属的类。

这意味着,不同的类中,可以定义同样的成员函数而互不影响。

 成员函数必须在类内部声明,可以在类内部定义,也可以在类外部定义。如果在类内部定义,就默认是内联函数。

在类内定义的函数默认为inline函数。

如果要在类外定义inline函数,需要在定义或者声明前加关键字inline。

 

二,访问控制和封装

1.将数据和操作捆绑在一起,并加上访问控制,称为封装

数据描述对象的属性,操作描述对象的行为

public,private,protected被称为访问限定符。

结构体以分号结束,类也以分号结束。

对象使用成员函数时使用成员运算符: .

对类的定义就是定义了一个具体的数据类型,要使用它我们必须将类实例化,即定义该类的对象。

 

2.this指针

this指针指向对象,它的值是当前被调用的成员函数所在的对象的起始地址。

一个经典的例子

假设你有一个图纸,你按照这个图纸造了许多房子,这些房子外部分别标了不同的名称以区别,但是它的内部陈设都一样;
当你进入房子时,你可以看见房子里的物品:桌子,凳子等,当你却看不到房子的全貌了,你对房子的“内部陈设”动了手脚,但此时你已经不知道你进入的是哪个房子了,动的是哪个房子的东西了

  • ​每个对象都拥有一个this指针,通过this指针来访问自己的地址​
  • ​每个成员函数都有一个指针形参​​​,它的名字是固定的,称为this指针,​​this指针是隐式的​​。(构造函数比较特殊,没有这个隐含this形参)
  • this指针是成员函数隐含指针形参,​​是编译器自己处理的​​,我们不能在成员函数的形参中添加this指针的参数定义,也不能在调用时显示传递对象的地址给this指针。
  • 编译器会对成员函数进行处理,在对象调用成员函数时,对象地址作实参传递给成员函数的第一个形参this指针,如图

c++类总结_数据

为什么需要this?因为this作用域是在类的内部,自己声明一个类的时候,还不知道实例化对象的名字,所以用this来使用对象变量的自身。在非静态成员函数中,编译器在编译的时候加上this作为隐含形参,通过this来访问各个成员(即使你没有写上this指针)。例如a.fun(1)<==等价于==>fun(&a,1)  

3.友元

友元一般存在于不同类之间,在一个类中,可以用全局函数作友元函数。而在不同类中,类成员函数作友元函数

友元可以是一个函数,该函数被称为友元函数,函数既可以是全局也可以是类的成员;友元也可以是一个类,该类被称为友元类。

同类对象间无私处,异类对象间有友元

友元目的本质:

是让其它不属于本类的成员(全局函数,其它类的成员函数),成为类的成员而具备了本类成员的属性。

友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下:

friend   类型函数名(形式参数);

例:

#include<iostream>
#include<bits/stdc++.h>
#include<string>
#include<algorithm>
using namespace std;

class Point; //由于manager中用到了Point类引用,引用是可以作前向声明的,P所以oint可以但是manager不行
class manager
{
public:
double getDistance(Point & a,Point & b);
};
class Point
{
public:
Point(double xx,double yy)
{
x=xx;
y=yy;
}
friend double manager::getDistance(Point & a,Point & b);
private:
double x,y;
};
double manager::getDistance(Point & a,Point & b)
{
double dx=a.x-b.x;
double dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
int main()
{

Point aa(1,2); Point bb(3,4);
manager cc;
double chen =cc.getDistance(aa,bb);
cout<<chen<<endl;
return 0;
}

输出结果:

c++类总结_构造函数_02

把一个类作为另一个类的友元

声明谁的友元,就可以通过谁的对象,访问谁的数据成员

例:

class Point
{
public:
Point(double xx,double yy)
{
x=xx;
y=yy;
}
friend class manager;
private:
double x,y;
};

class manager
{
public:
double getDistance(Point & a,Point & b);
double getTriDistance(Point & a,Point &b);
};

double manager::getDistance(Point & a,Point & b)
{
double dx=a.x-b.x;
double dy=a.y-b.y;
return sqrt(dx*dx+dy*dy);
}
double manager::getTriDistance(Point & a,Point & b)
{
}
int main()
{

Point aa(1,2); Point bb(3,4);
manager cc;
double chen =cc.getDistance(aa,bb);
cout<<chen<<endl;
return 0;
}

注意:

(1)友元关系不能被继承。
(2)友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3)友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

 

三,构造函数,析构函数

1.构造函数

<1>作用:赋初值,初始化对象的数据成员,由编译器帮我们调用。

    <2>特点:①函数名和类名一样。②没有返回值。③支持有参/无参。④可以重载。

          <3>调用时机:在类的对象创建时刻,编译器帮我们调用构造函数。

一个类内构造函数至少写两个,一个不带参数一个带参数。

例:

class Base
{
public:
Base(int var) : m_Var(var)
{
}
private:
int m_Var;
};

以上构造函数的执行过程:

1)传参

 2)给类数据成员开辟空间    
3)执行冒号语法给数据成员初始化    

4)执行构造函数括号里面的内容

这里需要说明的是:冒号语法后面的内容相当于int a = 10;(初始化),而构造函数括号里面则是相当于是int a; a = 10;(赋初值)

拷贝构造函数

class Base
{
public:
Base(int var) : m_Var(var)
{
}
//拷贝构造函数
Base(Base &ref) : m_Var(ref.m_Var)
{
}
private:
int m_Var;
};

拷贝构造函数的参数只能用引用

委托构造函数

 和其他构造函数一样,一个委托构造函数也有一个成员初始值列表和一个函数体。在委托构造函数内,成员初始值列表只有一个唯一的入口,就是类名本身。和其他成员初始值一样,类名后面紧跟圆括号括起来的参数列表,参数列表必须跟类中另外一个函数匹配。

例:

class Date

{

public:

//非委托构造函数使用对应的实参初始化成员

Date(int year,int month,int day)

:_year(year)

, _month(month)

, _day(day)

{}

//其余构造函数全都委托给另一个构造函数

Date() :Date(1990,1,1)

{}

Date(int year) :Date(){}

private:

int _year;

int _month;

int _day;

};

2.析构函数

 <1>作用:用于释放资源。    

    <2>特点:①和类名一样,不过得在前面加上~。②无参数,无返回值。③因为无参数,无返回值,所以不可以重载。④尽量不要自己调用析构           函数,但是在某些需要的时候再调用。     

            <3>调用时机:快退出函数的时候,编译器帮我们调用。

析构函数的名字是类名字前加波浪线“~”

 

四,const成员

1.const成员

在c++中不允许改变的数据成员,我们将它声明为const成员。 
const成员的声明形式是 
const+类型+变量名; 
const成员必须用初始化表的形式进行初始化。 

 

2.const成员函数

一次性不可修改

const 修饰成员函数表示传入函数内的为 const *this

const 成员函数:

(1)、不允许修改成员变量;

 (2)、mutable修饰符的成员变量,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的;

   (3)、不允许访问非const函数。

class Demo
{
public:
void print() const; //常成员函数
int get() const;
void set();
private:
int m_int;
mutable int m_mut;
};

void Demo::print(/* const Demo *this */) const //相当于传入了一个const 的this指针
{
m_int = 0;//error,(1)
set(); //error (2)
get(); //ok
m_mut = 20;//ok

const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数。

 

五,static成员

不属于某一个对象,属于全部对象共享。

Static数据成员在类定义时就存在了,所以有不同于一般数据成员的特性

1.      Static数据成员必须在类外部定义,且正好定义一次,static成员属于类,在类定义时初始化,而不是想普通数据成员在对象建立时通过构造函数初始化。所以static数据成员定义不应该放在头文件中,应该放在类非内联函数定义地方

2.      静态数据成员被类的所有对象所共享,包括该类派生类的对象。即派生类对象与基类对象共享基类的静态数据成员

3.      静态数据成员的类型可以是所属类的类型,而普通数据成员则不可以。普通数据成员的只能声明为 所属类类型的 指针或引用

4.       静态数据成员的值在const成员函数中可以被合法的改变(const函数只能禁止该对象值,却不能禁止该类的成员值)。

5.       静态数据成员可以成为成员函数的默认参数,而普通数据成员则不可以。

6.       一般数据成员只能在对象初始化时初始化,static成员在类定义时体外部初始化,而const static数据成员可以在类内部初始化

static成员函数

1.   static成员函数没有this指针,所以只能访问static数据成员

2.   静态成员函数不可以同时声明为 virtual、const、volatile函数。

class base{ 
virtual static void func1();//错误
static void func2() const;//错误
static void func3() volatile;//错误
};

3.   静态成员函数的地址可用普通函数指针储存,而普通成员函数地址需要用 类成员函数指针来储存。

例:

class base{ 
static int func1();
int func2();
};

int (*pf1)()=&base::func1;//普通的函数指针
int (base::*pf2)()=&base::func2;//成员函数指针