C++初识类和对象

目录
  • 一.初步认识面向过程和面向对象
  • 二.类的引入
  • 三.类的定义
    • 1.定义和声明全部放在类体中,需要注意的是:
    • 2.声明与定义分离
  • 四.类的访问限定符及封装
    • 1.访问限定符
    • 2.封装
  • 五.类的作用域
  • 六.类的实例化
  • 七.类对象模型
    • 1.计算类对象的大小
    • 2.类对象的存储方式
  • 八.this指针
    • 1.this指针的引出
    • 2.this指针的特性
  • 总结

一.初步认识面向过程和面向对象

面向过程,关注的是怎么去做,比如在外卖系统中,强调点餐,做餐,送餐等一系列动作的方法,反映到语言中是函数方法的实现;而面向对象,更关注的是谁去做,比如在外卖系统中,强调的是商家,买家和送货员之间的交互,反映到语言中则是对象的实现。

C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。

C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。

这里C++由于兼容C语言,因此既是面向过程,又是面向对象的,但是C++更关注的是对象,所以说C++是基于面向对象的。

二.类的引入

在C语言中,结构体只能定义变量,而在C++中,结构体升级为类,既可以定义变量,也可以定义函数:

struct Book
{
	void SetInfo(const char* name, const char* writer, double price)//建立书本信息
	{
		strcpy(_name, name);
		strcpy(_writer, writer);
		_price = price;
	}
	void PrintInfo()//打印书本信息
	{
		cout << _name << endl;
		cout << _writer << endl;
		cout << _price << endl;
	}
	char _name[20];
	char _writer[20];
	double _price;
};
int main()
{
	Book b1;
	Book b2;
	b1.SetInfo("老人与海", "海明威", 12.54);
	b2.SetInfo("骆驼祥子", "老舍", 14.88);
	b1.PrintInfo();
	cout << endl;
	b2.PrintInfo();
	return 0;
}

上面的结构体struct即为一个类,{}则形成了一个类域,{}中的内容为结构体Book的成员,既有成员变量,又有成员函数。在C++中,类通常用class这个关键字来表示。那么struct和class二者之间有什么区别呢?接下来我们就来介绍类。

三.类的定义

class className
{
 // 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号。

类中的元素称为类的成员:类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数。

类的两种定义方式:

1.定义和声明全部放在类体中,需要注意的是:

成员函数被定义在类体中,编译器会默认将其当作内联函数,其效用等同于函数前加上inline关键字,若不知道何为内联函数,可以参考之前文章中关于内联函数的介绍。

C++入门

2.声明与定义分离

声明放在头文件中,而定义放在源文件中。

通常情况下,为了代码的规范性,更倾向于采用第二种方法来实现类,并且代码较短的成员函数直接定义在类体中,而代码较长的函数定义在类体外。

需要注意的是,类中的成员变量均为声明,它们在实例化之前都未被分配空间,不能称作为定义。

四.类的访问限定符及封装

1.访问限定符

在之前我们讨论到class和struct之间有什么区别,那么这里我们将会介绍,首先,我们来了解以下类的访问限定符及封装。
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

访问限定符的说明:

1.public修饰的成员在类外可以直接被访问

2.protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)

3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止

4.在没有访问限定符的情况下,class的默认访问权限为private,struct为public(因为struct要兼容C)

注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。

由上面的说明我们就可以知道struct和class这两个关键字之间的区别在哪里了:实际使用过程中,struct和class定义类时并无区别,只是在二者均没有访问限定符的时候,struct默认的访问权限为public,而class默认的访问权限为private。

2.封装

我们知道面向对象有三大特性:封装,继承,多态。在类和对象阶段,我们研究类的封装特性。

首先,封装指的是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

其次,从本质上来说,封装是一种管理:举个例子,景区如果不加管理的话,那么景区的东西很可能会被不守规章制度的人破坏,这就好比C语言中为被封装的代码随时可能被修改,有时导致出现很大的错误;那么为了加强管理,保护景区,就需要设立景点售票口,同时安装监控和保安来保证景区不被破坏。

类也是如此,对于我们不想被随意修改的成员变量,我们用private表示其为私有,而为了使用者能够合理调用,我们将使用方法封装成一个个的接口即成员函数用public表示其为公用,至此我们将成员封装起来,同时开放一些公有的成员函数对成员合理的访问。所以封装本质是一种管理,使用封装可以是代码更加安全。

五.类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。

比如,在上面介绍类的第二种实现方式中的代码,在Book.cpp中定义函数ShowInfo时就是指定其为类域Book中的成员函数。

//Book.h
class Book//书
{
public:
	void ShowInfo();//展示书的信息
private:
	char* _name;//书名
	char* _writer;//作者
	double _price;//价格
//Book.cpp
#include "test.h"
void Book::ShowInfo()
{
	cout << _name << " " << _writer << " " << _price << endl;
}
};

六.类的实例化

用类类型创建对象的过程,称为类的实例化

1.类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它

2.一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量

3.打个比方,类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间

七.类对象模型

1.计算类对象的大小

类包含了成员变量和成员函数,那么类的大小应该如何计算呢?首先我们来看看下面这个代码的结果是什么:

class Book
{
public:
	void ShowInfo(){}
private:
	char* _name;
	char* _writer;
	double _price;
};
int main()
{
	cout << sizeof(Book) << endl;
	return 0;
}

可以看到,类Book的大小为16,那么这个16是怎么求出来的呢?

2.类对象的存储方式

我们再来看一个代码:

class C1//类中既有成员变量,又有成员函数
{
public:
	void fun();
private:
	int _a;
};
class C2//类中只有成员函数
{
public:
	void fun();
};
class C3//类中什么都没有,即空类
{
};
int main()
{
	cout << "C1:" << sizeof(C1) << endl;
	cout << "C2:" << sizeof(C2) << endl;
	cout << "C3:" << sizeof(C3) << endl;
	return 0;
}

它的结果是:

可以看到C1的大小为成员变量_a的大小,C2和C3的大小均为1,说明类的大小并不包括成员函数的大小,实际上如果类实例化时也会给成员函数开辟一块空间,那么当一个类创建多个对象时,每个对象中都会保存一份成员函数的代码,相同代码保存多次,浪费空间。

既然成员函数不在类的大小计算范围内,那么为什么空类的大小为1呢?这是因为一个类创建的时候需要开辟一块空间来占位,因此内存需要开辟一个字节,这个字节的空间是没有意义的,其不存储任何有效数据,但是其标识了空类的存在。

结论:一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。

八.this指针

1.this指针的引出

不知道你是否注意到,在C和C++实现栈的代码中,二者的函数参数有些许不同

可以看到,C++的函数参数比起C语言实现的函数都少了一个参数,那么问题来了,在下面代码中s1和s2都调用Init函数时,编译器是怎么区别是哪个变量调用的呢?这就是我们即将要介绍的this指针所起到的作用了。

int main()
{
	cpp::Stack s1;
	cpp::Stack s2;
	s1.Init();
	s2.Init();
	s1.Push(1);
	return 0;
}

实际上,C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

根据调试窗口可以看到this指针就是s1的地址,通过this指针可以访问s1。

2.this指针的特性

1.this指针的类型:类类型* const,比如上面代码中的this指针类型为Stack*

2.只能在“成员函数”的内部使用,this作为一个关键字不能拿它去当作变量的名字,其使用时可以显式的使用,比如:

		void Init()
		{
			this->_a = (int*)malloc(sizeof(int) * 4);
			this->_top = 0;
			this->_capacity = 4;
		}

3.this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。又因为this指针为形参,而形参和函数中的局部变量是存储在函数栈帧中的,因此this指针可以认为是存储在栈中的。

4.this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递.

我们最后再来看一个问题:this指针可以为空吗?

我们还是通过一个代码来看:

class A
{
public:
	void Show()
	{
		cout << "Show()" << endl;
	}
	void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A* p = nullptr;
	p->Show();
	p->Print();
	return 0;
}

那么p->Show(); p->Print();这两句代码能否运行成功呢?

可以看到第一句代码运行成功了,而第二句代码运行崩溃了。

这是因为成员函数的地址并不存在于对象中,而是存在于公共代码段;而上面的代码中调用函数时将p传给了隐含的this指针,并不会去访问p所指向的空间,就不存在空指针的解引用,因此程序可以并编译成功。而调用Show函数也没有对this指针解引用,因此程序运行成功了;调用Print函数则会对this指针解引用,故程序崩溃了。

结论:对于调用不会对this指针解引用的函数,this指针可以为空;而对于调用会对this指针解引用的函数,this指针不能为空。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • C++类和对象补充

    目录 一. 再看构造函数 1.函数体内赋初值 2.初始化列表 几点注意 3.explicit关键字 二.static成员 1.概念 2.特性 三.友元 1.友元函数 2.友元类 四.内部类 总结 一. 再看构造函数 我们之前已经了解了构造函数的基本内容,那么这里我们将深入认识构造函数. 1.函数体内赋初值 class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = d

  • C++类与对象之运算符重载详解

    目录 运算符重载 加号运算符重载 左移运算符重载 递增运算符重载 递减运算符重载 赋值运算符重载 关系运算符重载 函数调用运算符重载 总结 运算符重载 运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型 加号运算符重载 作用:实现两个自定义数据类型相加的运算 #include <iostream> using namespace std; class Person { public: // 构造函数 Person(int num1, int num2){ thi

  • C++进一步认识类与对象

    目录 赋值操作符重载函数 1.运算符重载 2.赋值运算符重载 3.默认的赋值操作符重载函数 4.赋值重载函数与拷贝构造函数的对比 日期类的实现 const成员 1.const修饰类的成员函数 2.小结 取地址及const取地址操作符重载函数 总结 赋值操作符重载函数 1.运算符重载 C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似. 其函数名为: operator + 需要重载的运算符符

  • C++ 再识类和对象

    目录 类的6个默认成员函数 构造函数 1.概念 2.特性 隐式构造函数 无参和全缺省的函数均为默认构造函数 成员变量的命名风格 补充 析构函数 1.概念 2.特性 c++编译器在对象生命周期结束时自动调用析构函数 拷贝构造函数 1.概念 2.特性 若未显式定义,系统会生成默认的拷贝构造函数 浅拷贝的注意事项 总结 类的6个默认成员函数 一个类中如果什么成员都没有,那么这个类称为空类.空类中是什么都没有吗?其实不然,任何一个类,再我们不写的情况下,都会自动生成下面6个默认成员函数: 本篇文章将对这

  • C++入门浅谈之类和对象

    目录 一.面向过程vs面向对象 二.类的限定符及封装 三.类的实例化 四.this指针 五.默认成员函数 1. 构造函数 2. 析构函数 3. 拷贝函数 4. 赋值运算符重载 总结 一.面向过程vs面向对象 C语言面向过程,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题 C++是基于面向对象,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成,C++不是纯面向对象的语言,C++既有面向过程,也有面向对象可以混合编程.C语言面向过程,数据和方法是分离的.CPP面向对象,数

  • C++初识类和对象

    目录 一.初步认识面向过程和面向对象 二.类的引入 三.类的定义 1.定义和声明全部放在类体中,需要注意的是: 2.声明与定义分离 四.类的访问限定符及封装 1.访问限定符 2.封装 五.类的作用域 六.类的实例化 七.类对象模型 1.计算类对象的大小 2.类对象的存储方式 八.this指针 1.this指针的引出 2.this指针的特性 总结 一.初步认识面向过程和面向对象 面向过程,关注的是怎么去做,比如在外卖系统中,强调点餐,做餐,送餐等一系列动作的方法,反映到语言中是函数方法的实现:而面

  • Python面向对象编程中的类和对象学习教程

    Python中一切都是对象.类提供了创建新类型对象的机制.这篇教程中,我们不谈类和面向对象的基本知识,而专注在更好地理解Python面向对象编程上.假设我们使用新风格的python类,它们继承自object父类. 定义类 class 语句可以定义一系列的属性.变量.方法,他们被该类的实例对象所共享.下面给出一个简单类定义: class Account(object): num_accounts = 0 def __init__(self, name, balance): self.name =

  • 关于JavaScript定义类和对象的几种方式

    可以看看这个例子: 复制代码 代码如下: var a = 'global'; (function () { alert(a); var a = 'local'; })(); 大家第一眼看到这个例子觉得输出结果是什么?'global'?还是'local'?其实都不是,输出的是undefined,不用迷惑,我的题外话就是为了讲这个东西的. 其实很简单,看一看JavaScript运行机制就会明白.我们可以把这种现象看做"预声明".但是如果稍微深究一下,会明白得更透彻. 这里其实涉及到对象属性

  • JavaScript定义类和对象的方法

    本文实例讲述了JavaScript定义类和对象的方法.分享给大家供大家参考.具体方法如下: 在JS中,类和对象有多种不同的写法,因为本人对JS也不怎么熟,所以就本人的理解来写,如果哪位朋友发现有不对,请告之,共同学习. JS定义一个类有两种定法(我只知道这两种): 1. 定义函数的方式: 定义: 复制代码 代码如下: function classA(a) {      this.aaa=a;  //添加一个属性      this.methodA=function(ppp)  //添加一个方法

  • javaScript中定义类或对象的五种方式总结

    第一种方式: 工厂方法 能创建并返回特定类型的对象的工厂函数(factory function). function createCar(sColor){ var oTempCar = new Object; oTempCar.color = sColor; oTempCar.showColor = function (){ alert(this.color); }; return oTempCar; } var oCar1 = createCar(); var oCar2 = createCa

  • Javascript学习笔记5 类和对象

    面向对象语言三大特点:继承,多态,封装,这三点虽然Javascript没有提供天然的语法实现,但是我们都可以通过prototype等技巧来实现,因此这种说法似乎不过分. 在Javascript中,构造对象有三种方式: 1. 首先,我们要明确一个概念,Javascript是一种弱类型的语言,一方面体现在Javascript的变量,返回类型都是没有强类型约束的,另一方面,Javascript可以为对象任意添加属性和方法.根据这个,我们可以写出这样的代码: 复制代码 代码如下: <script typ

  • Javascript创建类和对象详解

    现总结一下Javascript创建类和对象的几种方法: 1.原始的创建方法: <script type="text/javascript"> var person = new Object(); person.name="Amy"; person.sex="Woman"; person.show=function(){ document.write("name is: "+this.name+" ; se

  • js 用于检测类数组对象的函数方法

    如下所示: //判定o是否是一个类数组对象 //字符串和函数有length属性,但是它们 //可以用typeof检测将其排除.在客户端JavaScript中,DOM文本节点 //也有length属性,需要用额外判断o.nodeType!=3将其排除 function isArrayLike(o){ if(o&&//o非null/undefined等 typeof o==="object"&&//o是对象 isFinite(o.length)&&a

  • php基础知识:类与对象(5) static

    Declaring class members or methods as static makes them accessible without needing an instantiation of the class. A member declared as static can not be accessed with an instantiated class object (though a static method can).  声明静态的类变量和方法可以不需要实例化类对象的

  • php使用变量动态创建类的对象用法示例

    本文实例讲述了php使用变量动态创建类的对象.分享给大家供大家参考,具体如下: 这是一个能用变量动态创建类的对象的用法,就是根据$pay_code变量值来创建对象. 例如下例就是创建类T的一个对象$payment.然后就可以使用了.咋一看像是个新的知识点,其实不然,只不过是编程中的一个技巧而已. $pay_code='T'; $payment = new $pay_code(); echo $payment; echo $payment->ep(); class T{ function ep()

随机推荐