14.1 C++类-成员函数、对象复制与私有成员

文章介绍了C++中的类基础,包括成员函数、对象复制和私有成员的概念。成员函数作为类的功能实现,可以通过对象或指针调用。对象复制涉及到浅拷贝和深拷贝,私有成员则限制了外部对类内部细节的访问。文章还提到了类的访问控制,如public和private成员的作用,以及构造函数和析构函数在对象生命周期中的重要性。
摘要由CSDN通过智能技术生成

14.1 C++类-成员函数、对象复制与私有成员
14.2 C++类-构造函数详解、explicit与初始化列表
14.3 C++类-inline、const、mutable、this与static
14.4 C++类-类内初始化、默认构造函数、“=default;”和“=delete;”
14.5 C++类-拷贝构造函数
14.6 C++类-重载运算符、拷贝赋值运算符与析构函数
14.7 C++类-子类、调用顺序、访问等级与函数遮蔽
14.8 C++类-父类指针、虚/纯虚函数、多态性与析构函数
14.9 C++类-友元函数、友元类与友元成员函数
14.10 C++类-RTTI、dynamic_cast、typeid、type-info与虚函数表
14.11 C++类-基类与派生类关系的详细再探讨
14.12 C++类-左值、右值、左值引用、右值引用与move
14.13 C++类-临时对象深入探讨、解析与提高性能手段
14.14 C++类-对象移动、移动构造函数与移动赋值运算符
14.15 C++类-继承的构造函数、多重继承、类型转换与虚继承
14.16 C++类-类型转换构造函数、运算符与类成员指针

1.成员函数、对象复制与私有成员

  1.1 总述

    类是一种自定义的数据类型,也就是一个新类型。类与类之间,又不是彼此孤立的,例如说一个类可以派生出子类,那么这个派生出子类的类就变成了该子类的父类。
    在设计一个类的时候要站在很多角度去考虑,这里先列举出几个比较简单容易理解的角度。
    (1)站在设计和实现者的角度来考虑,也就是自己。如何理顺在设计一个类的时候这个类里的数据存储布局,有哪些必要的成员变量和成员函数要定义和实现。
    (2)站在使用者的角度来考虑,需要给使用者提供哪些可以访问的接口,而哪些接口不对外开放,只供类内的其他成员函数使用。
    (3)在设计一个父类供子类继承的时候,如何设计这个父类。例如设计一个车的类,然后将来可能很多子类会继承该类,如轿车、卡车、摩托车等,那么,设计父类时,可能就要把一些车的公共特性抽取出来放到父类中,如都用油来驱动、都要在机动车道上行驶等这些公共特性。
    因为可以有很多的角度来考虑类的设计,所以就产生了很多的设计思想和设计概念,这些设计概念比较抽象,读者刚刚接触类,先不必了解这些思想,而是把类的基础知识学好。

  1.2 类基础

    (1)一个类就是一个用户自己定义的数据类型,可以把类想象成一个名字空间,包着一堆内容(成员函数、成员变量)。
    (2)一个类的构成,最常见的就是成员变量、成员函数这两种。当然,也有很多特殊的成员函数,它们的名字也特殊,功能也特殊,后续会慢慢讲。
    (3)访问类的成员(成员函数、成员变量)时,如果用类的对象来访问,就使用“对象名.成员名”来访问成员。如果用指向这个对象的指针来访问,就使用“指针名->成员名”来访问成员。

struct student
{
	int number;
	char name[100];
	void func() {};
};
int main()
{
	student student1;  //定义结构变量。这里可以省略struct,直接用结构名student
	student1.number = 1001;
	strcpy_s(student1.name, sizeof(student1.name), "zhangsan");
	student1.func(); //调用成员函数
	student* pstudent1 = &student1;
	pstudent1->number = 1005;
	cout << student1.number << endl; //1005
}

    (4)类中public修饰符修饰的成员提供类的访问接口,暴露给外界,供外界调用,private成员提供各种实现类功能的细节方法,但不暴露给外界(使用者),外界无法使用这些private成员。
    (5)struct(结构)是成员默认为public的class(类)。struct的定义一般如下:

struct A{...};

(6) class成员默认是private的。class的定义一般如下:

class A{...};

    上面定义了一个名为A的类,所以,A就成了一个类型,后续就可以拿来使用。如此看来:

struct A{...};

    等价于

class A{public:...};

    建议写代码时尽量不要将class和struct混用,否则代码会显得比较混乱。当然,如果把没有成员函数只有成员变量的数据结构定义为struct,而把有成员函数的数据结构定义为class也是可以的,那就在编写代码中一直遵循这个规则。总之,有一个共同遵循的标准,而不是随意混用struct和class就好。

  1.3 成员函数

    看如下代码:

//定义一个Time类
class Time 
{
public:
	int Hour;
	int Minute;
	int Second;
};
//定义一个函数InitTime
void initTime(Time& stmptime, int tmphour, int tmpmin, int tmpsec)
{
	stmptime.Hour = tmphour;
	stmptime.Minute = tmpmin;
	stmptime.Second = tmpsec;
}
int main()
{
	Time myTime;
	initTime(myTime, 11, 14, 5);
	cout << myTime.Hour << endl;  //11
	cout << myTime.Minute << endl; //14
	cout << myTime.Second << endl; //5	
}

    可以注意到,这样写程序是C语言中的写法,类Time和initTime函数之间没有什么直接的关联关系。但显然,Time类和initTime函数之间应该有关联关系。如果把initTime函数设计为类Time的成员函数,那么两者就有关联关系了。调整Time类代码:

class Time {
public:
	int Hour;
	int Minute;
	int Second;
	void initTime(int tmphour, int tmpmin, int tmpsec) //成员函数
	{
		Hour = tmphour;
		Minute = tmpmin;
		Second = tmpsec;
	}
};
int main()
{
	Time myTime;
	myTime.initTime(11, 14, 5);  //这就是调用成员函数(使用成员函数)
	cout << myTime.Hour << endl;  //11
	cout << myTime.Minute << endl; //14
	cout << myTime.Second << endl; //5
}

    如果遵从常规的书写规范,把类定义和类实现放在分开的.h头文件和.cpp源文件中,看看应该怎样写。在Time.h文件中,内容如下:

#ifndef __MYTIME__
#define __MYTIME__
class Time {
public:
	int Hour;
	int Minute;
	int Second;
	void initTime(int tmphour, int tmpmin, int tmpsec);
};
#endif

    在Time.cpp文件中,内容如下(记得要把Time.cpp加入到项目中来):

#include "Time.h"

//其中这两个冒号叫:作用域运算符,表示initTime函数属于Time类。可能有多个不同的类,其他类中也可能有叫initTime()的成员函数,所以这里必须用Time::来表明该函数属于Time类
void Time::initTime(int tmphour, int tmpmin, int tmpsec) 
{
	Hour = tmphour;  //注意到,成员函数中可以直接使用成员变量名
					 //哪个对象调用的该成员函数,那么这些成员变量就属于哪个对象。可以理解成类成员函数知道哪个对象调用的自己
	Minute = tmpmin;
	Second = tmpsec;
}

    主cpp文件(MyProject.cpp)的上面位置也要增加如下代码:

#include "Time.h"

    其他代码都不变,编译链接后,程序可以正常执行。
    读者可能会有一点疑惑:类定义放在一个头文件中,多个.cpp文件都包含这个头文件,那不就相当于这个类定义了多次吗?读者都知道,一个全局变量不允许定义多次,一个类难道允许定义多次?
    确实允许定义多次,类是一个特殊的存在,在多个不同的.cpp源文件中用#include重复类定义是被系统允许的,这一点与定义一个全局变量不同。所以许多人把类定义也称为“类声明”。

  1.4 对象的复制

看如下代码:

{
	Time myTime2 = myTime;
	Time myTime3(myTime);
	Time myTime4{ myTime };
	Time myTime5 = { myTime };
	myTime5.Hour = 8;

	Time myTime6;
	myTime6 = myTime5;  //通过赋值操作来拷贝对象
}

    可以注意到,对象是可以复制的,上面这几种写法都是对象复制,复制后,每个对象都有不同的地址(每个对象的内容都保存在不同的内存中,彼此互不影响),而且成员变量的值都相等。
    对象的复制,就是定义一个新对象时,用另外一个老对象里面的内容进行初始化。在写法上,观察上面的代码,对象的复制可以使用“=”“()”“{}”“={}”等运算符进行。同时,上面的代码也演示了通过赋值运算符来做对象的复制。
    默认情况下,这种类对象的复制是每个成员变量逐个复制,如Time类中的Hour、Minute、Second。那能否控制对象的这种逐个成员变量复制的行为呢?能!只要给这个类定义一个恰当的“赋值运算符(或类似的内容)”,就能够控制对象的复制行为,这个后面再讲,读者先有个印象,这里提到的这个恰当的赋值运算符可以理解为一个成员函数,这个成员函数负责控制复制哪些成员变量。

  1.5 私有成员

在Time类(Time.h)中增加一些成员,看如下代码:

class Time {
public:
	//......
private:
	int Millisecond;
private:
	void initMillTime(int mls);
};

    类的私有成员变量和私有成员函数都只能在类的成员函数内调用,外界是无法直接调用的。
    修改Time.cpp中的Time::initTime成员函数,在其中增加对initMillTime成员函数的调用,同时,增加initMillTime成员函数的实现代码:

void Time::initTime(int tmphour, int tmpmin, int tmpsec) 
{
	Hour = tmphour; 
	Minute = tmpmin;
	Second = tmpsec;
	initMillTime(0);
}
void Time::initMillTime(int mls)
{
	Millisecond = mls;
}

    私有成员的设置目的,主要是希望这些接口不暴露在外面,不被其他使用者所知和所用(假设自己所开发的类即将被他人使用),只为类内部的其他成员函数使用。
    所以,在设计类成员的时候要好好思考,对外暴露哪些接口,哪些接口是类内部使用的。这样才更利于设计出优质的类结构和写出优质的代码。
    在类定义内部,private和public修饰符修饰其下面的所有成员,一直遇到其他的public或者private修饰符。因为定义class时,默认所有成员为private,所以不加public修饰的成员全部都是private的。
    此外,一个类的定义中可以出现多个public、多个private,这都被系统所允许。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值