怎样使用类和对象

目录

利用构造函数对类对象进行初始化

1、对象的初始化

2、构造函数实现数据成员的初始化

析构函数进行清理工作

调用构造函数和析构函数的顺序

对象数组

对象指针

公用数据保护

对象的动态建立和释放

对象的赋值和复制

静态成员

友元

类模板


利用构造函数对类对象进行初始化

1、对象的初始化

不可以在声明成员变量的时候对数据成员初始化:因为类不是实体,是一个抽象类型,不占用内存空间,无法容纳数据。

当把数据成员声明为public时,可以用花括号“{}”,顺序列出各个公有成员的值,两个值用逗号隔开

上述的对象初始化类似于结构体

2、构造函数实现数据成员的初始化

构造函数:是一个特殊的成员函数,不需要用户调用,在建立对象时自动执行

构造函数的一些说明:

  1. 定义对象是自动调用构造函数,而且只能执行一次,构造函数一般是public
    解释说明:
    在建立类对象时,系统为该对象分配存储单元,此时执行构造函数,然后把指定的初始值送入到有关数据成员的存储单元中
  2. 构造函数没有返回值,它的作用只是对对象进行初始化
  3. 构造函数不能被用户调用
  4. 可以用一个类对象初始化另一个对象,如    
    class Time time1;
    class Time time2 = time1;
  5. 构造函数不仅可以对数据成员赋值,而且可以包含其他语句,如,cout等,
    但是一般不提倡在构造函数中加入与初始化无关的内容
  6. 如果用户自己没有定义构造函数,则C++系统会自动生成一个构造函数,只是函数体为空,参数为空,不执行初始化

3、带参数的构造函数

构造函数名(类型1 参数1,类型2 参数2 ,。。。)

类名 对象名(实参1,实参2,。。。);

在定义对象时给出实参,传递给构造函数的形参中,实现对象的初始化。

//类声明头文件
Box(int ,int ,int );

//类实现文件
Box::Box(int length,int width,int height){
	this->length = length;
	this->width = width;
	this->height = height;
}

//主函数文件
class Box box1(1,2,3);

4、带参数初始化表对数据成员初始化

参数初始化表实现对数据成员的初始化。

类名::构造函数名([参数表])[:成员初始化表]

{

          [函数体]

}

[]  表示可有可无

//类声明头文件
Box(int ,int ,int);

//类定义文件
Box::Box(int len,int w,int h):length(len),width(w),height(h){}

//主函数文件
class Box box1(1,2,3);

5、构造函数的重载

构造函数的重载:在一个类中定义多个构造函数,这些构造函数具有相同的名字,参数的个数或参数的类型不相同

//类声明的头文件
	Box();
	Box(int);
	Box(int ,int ,int);

//类实现文件

//无参的构造函数
Box::Box()
{
	this->length = 10;
	this->width = 10;
	this->height = 10;
}
//有参的构造函数
Box::Box(int h):height(h){
	this->length = 10;
	this->width = 10;

}
//有参的构造函数
Box::Box(int len,int w,int h):length(len),width(w),height(h){}

//主函数文件

class Box box1;
cout<<box1.volume()<<endl;
class Box box2(5);
cout<<box2.volume()<<endl;
class Box box3(1,2,3);
cout<<box3.volume()<<endl;

说明:

  1. 建立对象时不必给出实参的构造函数为默认构造函数
  2. 建立对象时选用无参的构造函数时,应当注意:
    class Box box1;        //没有括号
  3. 尽管一个类中有多个构造函数,但是对每一个对象来说,只执行一个构造函数

6、使用默认参数的构造函数

1、声明有默认参数的构造函数时,形参名可以省略

//以下两种均可
Box(int len = 5,int w = 5,int h = 5); 
Box(int = 5,int = 5,int  = 5);

2、如果构造函数的全部参数都指定了默认值,则在定义对象是可以给出一个或几个实参,也可以不给出实参
     如果构造函数的全部参数都指定了默认值:默认构造函数
     一个类中只能有一个默认构造函数

注意:以下代码中给出了两个默认的构造函数,是错误的;

在调用时出错:对重载函数的调用不明确

//类声明文件
	Box();
	Box(int = 5,int = 5,int  = 5);

3、如果构造函数的全部参数都指定了默认值,不能再定义重载构造函数

总结:一般不同时使用默认参数的构造函数和构造函数的重载


 

析构函数进行清理工作

析构函数:在对象生命期结束时,自动执行析构函数

                   没有返回值,没有函数类型,没有函数参数,不能重载

在一个类中可以有多个构造函数,但是只有一个析构函数

当对象声明周期结束,自动执行析构函数,具体一下集中情况:

  1. 在一个函数中定义了对象(自动局部对象),函数被调用结束,对象应该释放,在对象释放前自动执行析构函数
  2. 使用类名定义一个临时对象,临时对象的作用域仅仅在这一行,先使用,之后直接析构
  3. 静态局部对象在函数调用结束时对象也不释放,因此也不调用析构函数,只有main函数结束或exit函数结束时,才调用static局部对象的析构函数
  4. 如果定义了一个全局对象,在main函数结束或exit函数结束时,才调用全局对象的析构函数
  5. 如果使用new运算符动态的简历一个对象,当用delete运算符释放该对象是,先调用该对象的析构函数 

析构函数的作用:

  1. 释放资源
  2. 对象结束之后的操作,如输出有关信息

举例说明:在下面定义中,使用了成员函数(指针),在析构函数中delete ptr,同时给出了回执信息

//类定义头文件
public:
	Box(int = 5,int = 5,int  = 5);
	~Box();
private:
	int *ptr;

//类实现文件
Box::Box(int len,int w,int h):length(len),width(w),height(h)
{
	this->ptr = new int(10);
}
Box::~Box()
{
	delete ptr;
	cout<<"调用了析构函数"<<endl;

}

调用构造函数和析构函数的顺序:先构造后析构,后构造先析构。类似于栈,先进后出


对象数组

建立对象数组时,需要调用构造函数,如果有n个元素,则调用n次构造函数

class Box box[10];中需要调用10次构造函数

对象数组的定义:

  1. 如果构造函数只有一个参数,则可以在花括号“{}”内提供实参,如Age age[3] = {10, 20, 10};
  2. 如果构造函数有多个参数,不可以向上述那样。否则,编译系统会根据花括号“{}”内的数据,给每一个对象元素的构造函数传递第1个实参。
    如,类Box的构造函数中有3个参数,
    class Box box[3] = {10,20,15};表达的意思是:box[0]的第一个实参是10.

                                                                                      box[1]的第一个实参是20.             box[2]的第一个实参是15.

         如果class Box box[3] = {10,20,15,30};直接报错:实参个数不能超过数组元素的个数,此时 4 > 3

      3.对象数组的正确定义:
          class Box box[3] = {Box(10,20,15),Box(20,20,15),Box(10,15,15)};


对象指针

1、指向对象的指针

对象的指针:一个对象存储空间的起始地址

类名 *对象指针名                      class Box *ptr

 

2、指向对象成员的指针

1)、指向对象数据成员的指针变量

数据类型名 *指针变量名

int *ptr;

ptr = &box1.height;

2)、指向对象成员函数的指针变量

插入函数基础知识:

函数:int fun(int,int,int){},

函数的地址:fun或&fun

函数的类型 :int (int ,int ,int)

函数的类型 = 函数声明-函数名;

函数的类型 = 函数参数的类型 + 函数参数的个数 + 函数的返回值类型

定义函数指针:

	int (*ptrFunc)(int,int,int);
	ptrFunc = &fun;
	cout<<ptrFunc(5,8,9)<<endl;

 

定义指向公共函数成员的指针变量:

数据类型名 (类名::*指针变量名)(参数列表)

指针变量明 = &类名::成员函数名

	int (Box::*ptrFunc)();
	ptrFunc = &Box::volume;
	class Box box;
	cout<<(box.*ptrFunc)()<<endl;

在代码的第二行有取地址符(&)和第四行有指针运算符(*)应该相互呼应。

同时优先级   ()   >   *,所以第四行需要添加括号(box.*ptrFunc),之后再和后面的括号()结合

3、指向当前对象的this指针

this指针:指向当前对象,保存当前对象的地址

类型:类名 *

类成员函数的形参列表中的第一个参数(隐含的参数)

int Box::volume(){
	return this->length * this->width * this->height;
}

C++编译器处理为:

int Box::volume(Box *this){
	return this->length * this->width * this->height;
}

先有对象,然后有this指针

编译器的调用过程:box.volume(&box) , this = &box,然后才是成员变量或函数成员

this指针比较特殊的使用方式:

返回当前对象的地址和返回当前的对象

//类声明文件
	Box* getAddr();//返回当前对象的地址
	Box getSelf();//返回当前对象

//类实现文件
Box* Box::getAddr()//返回当前对象的地址
{
	return this;
}
Box Box::getSelf()//返回当前对象
{
	return *this;
}

//主函数

	class Box * ptr;
	class Box box;
	ptr = box.getAddr();
	cout<<ptr<<"         "<<&box<<endl;
	box = box.getSelf();

 

 

公用数据保护

1、常对象

类名 const 对象名[(参数列表)]            或者          const 类名 对象名[(参数列表)]

2、常对象成员

1)常数据成员

const  类型 变量名

常数据成员只能通过参数初始化列表实现初始化

const int hour
Time::Time(int h):hour(h){}

2)常成员函数

类型名 函数名(参数表)const

//类声明文件
int volume() const;

//类实现文件
int Box::volume() const{
	return this->length * this->width * this->height;
}

//main函数
	class Box box;
	cout<<box.volume()<<endl;

总结:

常对象只能访问常成员函数

常成员函数只能引用本类中的数据成员,而不能修改他们

常成员函数不能调用非const成员函数

3、指向对象的常指针

类名 * const 指针变量名

解释:将指针变量声明为const类型, 这样的指针变量始终保持初值,不可改变,始终保持指向同一个对象,但是可以改变其指向对象的值;

	class Box box1;
	class Box* const ptr = &box1;      //ptr始终如一,不可改变指向
	cout<<ptr->volume()<<endl;
	box1 = Box(2,5,8);                 //改变了其指向对象的值,使用了临时对象,所以调用了析构
	cout<<ptr->volume()<<endl;

作用:通常使用常指针为形参,保证在函数执行过程中不改变指针变量的值,使其始终指向原来的对象

4、指向常对象的指针变量

1、指向常变量的指针变量

常变量的定义:const 类型名 变量名

指向常变量的指针变量:const 类型名* 指针变量名

1、如果一个变量为常变量,那么只能用指向常变量的指针变量指向它

2、指向常变量的指针变量也可以指向非const的变量,不能通过指针变量来改变指向变量的值

3、函数的形参是普通(非const)的指针变量,实参只能用指向普通的指针变量,而不能用const变量的指针

2、指向常对象的指针变量

常对象的定义:const 类名 对象名([参数表])

指向常对象的指针变量:const 类名 *指针变量明

1、如果一个对象为常对象,那么只能用指向常变量的指针变量指向它

2、指向常对象的指针变量也可以指向非const的对象,不能通过指针变量来改变指向对象的值,但是指针变量本身的值可以修改

	const int a1 = 8;
	const int *ptrInt = &a1;
	cout<<*ptrInt<<endl;
	int a2 = 10;
	ptrInt = &a2;
	cout<<*ptrInt<<endl;

3、指向常对象的指针通常用于函数的形参,保证形参所指向的对象,使其在函数执行过程中不被修改
      一般是show ,display()等函数。

 上述的结论是:若希望在调用函数时对象的值不被修改,应把形参定义为指向常对象的指针变量,同时用对象的地址为实参(对象可以是const和非const型)

5、对象的常引用

SVVVIP:
对象的常引用和常指针作为函数参数,在保证数据安全,不被随意修改,同时不必建立实参的拷贝,提高了程序的运行效率。

 

常对象:const 类名 对象名([实参表])               定义时必须初始化                                     指向常对象只能是常指针

常指针:const 类名 *ptr                                         可以指向const对象和非const对象                            

常引用:const 类名 &引用名                                  可以引用const对象和非const对象

常函数:返回值类型  函数名([参数表]) const         可以引用数据成员,但不可修改;且不可调用非const函数

常数据:const 类型  变量名                                  只能通过参数初始化列表实现初始化


对象的动态建立和释放

采用new和delete,实现动态建立和释放。

第一行代码:建立对象,初始化,

第二行代码:释放对象,释放空间之前会自动调用析构函数(delete语句会触发析构函数)

	class Box *ptr = new Box(12,5,16);
	delete ptr;

对象的赋值和复制

1、对象的赋值

对象的赋值:一个对象中数据成员的值赋给另一个对象数据成员

对象的赋值的原理是赋值运算符的重载

  1. 对象的赋值只对其中数据成员(非静态数据成员)的赋值,而不是对成员函数的赋值。
  2. 类的数据成员中不能包括动态分配的数据(用new/malloc时开辟,delete/free时释放),否则赋值时可能出现严重后果(容易出现野指针)

2、对象的复制

类名 对象2(对象1)          class Box box2(box1);

类名 对象2 = 对象1        class Box box2 = box1;

复制构造函数(拷贝构造函数):只有一个参数,在进行对象复制时,主动调用拷贝构造函数,而不去调用构造函数

Box(const Box &box);

构造函数和拷贝构造函数的区别:

1)、形式上:类名(形参列表)

                     类名(类名 & 对象名)

2)、建立对象时,实参类型不同。   Box box1(12,13,45);

                                                        Box box1(&box2);

3)、调用的时间点

构造函数在对象建立的时候被调用

拷贝构造函数在用已有的一个对象复制另外一个新对象时被调用,具体分为三种情况

  • 程序建立一个新的对象,需要用另一个对象去初始化
  • 当函数的参数为类的对象时(普通的形参,不是引用,不是指针)
  • 函数的返回值为类的对象时(因为在函数结束的时候,需要将返回值带回函数调用处,但是函数中的对象的作用域已经结束并释放,这时需要将函数中的对象复制(调用拷贝构造函数)一个临时对象并传递给函数的调用处)

静态成员

静态数据成员

1、静态数据成员不属于某个对象的成员,在所有对象之外开辟空间。

2、在一个函数中定义了静态变量,在函数结束时该静态变量并不释放,仍然存在并保留其值;

      在一个类中定义了静态数据成员,在程序编译时被分配空间,到程序结束时才释放空间

3、在类体中定义静态成员变量                static 类型 静态成员名

3、静态数据成员只能在类体外初始化    类型 类名::静态成员名 = 初值         类体中用static定义,类体外不用添加static

                                                                int Box::radius = 10;                        不可以用参数初始化表对静态数据成员初始化

4、静态数据成既可以通过对象名引用,也可以通过类名引用                        box1.radius            或者           Box::radius

5、如果静态数据成员为private时,则不能通过类名引用,只能通过公用的成员函数引用

6、静态数据成员的作用域仅限于类的作用域(如果在一个函数中定义了一个类,那么静态数据成员的作用域在此函数中)

static成员必须在类外进行初始化(初始化格式: int Box::radius = 10;),而不能在构造函数内进行初始化,不过也可以用const修饰static数据成员在类内初始化 。因为静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。

静态成员函数

1、静态成员函数是类的一部分而不是对象的一部分,使这个类只存在这一份函数,所有对象共享该函数,不含this指针

2、static声明静态成员函数                          static 类型 函数名([参数列表])

3、静态成员数据的定义                               类型  类名::函数名([参数列表])

4、因为静态成员函数不属于某个对象,所以为静态成员函数没有this指针

5、静态成员函数主要访问静态数据成员,而不访问非静态成员

(因为没有this指针,不知道非静态成员属于哪个对象的this指针)

6、如果静态成员函数想访问非静态成员,需要在形参中传入对象(常引用,常指针),使得非静态成员属于那个对象,并用        对象名和成员运算符(box.height)

7、当static成员函数在类中声明时使用static修饰,而类外定义时不需要加static修饰符

8.、上述7和inline正好相反,inline在类声明,在类外定义时需要加inline修饰符

结论:C++只用静态成员函数引用静态成员,而不引用非静态数据成员。

友元

1、友元函数

在类体中用friend声明,在类体外定义且没有用类名限定——>此函数是本类的友元函数。

解释下列代码:

display函数是Time类中friend函数,所以display函数可以访问私有成员

display函数的定义没有被Time限定,所以display函数不是Time类的成员函数,没有this指针,不可以默认引用,必须指明访问的对象(也就是作为形参的常引用)

//类声明文件
#include<iostream>
using namespace std;
class Time
{
public:
	Time(int,int,int);
	friend void display(const Time &);
private:
	int hour;
	int min;
	int sec;
};

//类实现文件
#include<iostream>
#include "time.h"
using namespace std;
Time::Time(int h,int m,int s):hour(h),min(m),sec(s){}
void display(const Time &time)
{
	cout<<time.hour<<":"<<time.min<<":"<<time.sec<<endl;

}

//主函数文件
#include<iostream>
#include "time.h"
using namespace std;
int main(){
	class Time time(22,25,30);
	display(time);
	system("pause");
	return 0;

}

2、友元成员函数

friend函数可以为一般函数,而且也可以是另一个类的成员函数


类模板

即使是在创建非内联函数定义时,我们还是通常想把模板的所有声明都放入一个头文件中。这似乎违背了通常的头文件规则:“不要放置分配存储空间的任何东西“(这条规矩是为了防止在连接期间的多重定义错误),但模板定义很特殊。由template<…> 处理的任何东西都意味着编译器在当时不为它分配存储空间,它一直处于等待状态直到被一个模板实例告知。在编译器和连接器的某一处,有一机制能去掉指定模板的多重定义。所以为了容易使用,几乎总是在头文件中放置全部的模板声明和定义。

类模板是类的抽象,类是类模板的实例

类模板声明:

template <typename T>
class Compare
{
public:
	Compare(T ,T );
	T max();
	T min();
private:
	T x;
	T y;
};

类模板定义:

类模板定义外定义成员函数:

template <typename 虚拟类型参数>

函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表){.......}

template <typename T>
Compare<T>::Compare(T a,T b):x(a),y(b){}

template <typename T>
T Compare<T>::max(){
	if(this->x > this->y) return this->x;
	else return this->y;
}

template <typename T>
T Compare<T>::min(){
	if(this->x < this->y) return this->x;
	else return this->y;
}

类模板中有多个类型,且还有默认值:

只需要在类模板中使用默认模板参数,必须从右向左连续赋值默认类型

sing namespace std;
template <typename T,typename Y = int>
class Compare
{
public:
	Compare(T ,Y );
	T max();
	T min();
private:
	T x;
	Y y;
};

template <typename T,typename Y>
Compare<T,Y>::Compare(T a,Y b):x(a),y(b){}

template <typename T,typename Y>
T Compare<T,Y>::max(){
	if(this->x > this->y) return this->x;
	else return this->y;
}

template <typename T,typename Y>
T Compare<T,Y>::min(){
	if(this->x < this->y) return this->x;
	else return this->y;
}

类模板定义对象:

类模板名 <实际类型名> 对象名;

类模板名 <实际类型名> 对象名(实参表);

class Compare<int> compare(5,40);                //可以省略一个,有了默认
class Compare<int , char> compare(5,40);         //覆盖默认

class Compare<int> * ptr = new Compare<int>(5,40);           //可以省略一个,有了默认
class Compare<int,int> * ptr = new Compare<int,int>(5,40);   //覆盖默认

总程序:

//模板类声明与定义,必须同时放在头文件中

#include<iostream>
using namespace std;
template <typename T,typename Y = int>
class Compare
{
public:
	Compare(T ,Y );
	T max();
	T min();
private:
	T x;
	Y y;
};

template <typename T,typename Y>
Compare<T,Y>::Compare(T a,Y b):x(a),y(b){}

template <typename T,typename Y>
T Compare<T,Y>::max(){
	if(this->x > this->y) return this->x;
	else return this->y;
}

template <typename T,typename Y>
T Compare<T,Y>::min(){
	if(this->x < this->y) return this->x;
	else return this->y;
}

//主函数文件

#include<iostream>
#include "compare.h"
using namespace std;
int main()
{
	class Compare<int> compare(5,40);
	//class Compare<int,int> compare(5,40);
	class Compare<int,int> * ptr = new Compare<int,int>(5,40);
	//class Compare<int> * ptr = new Compare<int>(5,40);
	cout<<ptr->max()<<endl;
	cout<<compare.max()<<endl;
	cout<<compare.min()<<endl;

	system("pause");
	return 0;

}

 

 

 

 

 

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值