c++ 类和对象总结

所属分类: 软件编程 / C 语言 阅读数: 105
收藏 0 赞 0 分享

话不多说,我们直接进入主题:
对象:客观世界里的一切事物都可以看作是一个对象,每一个对象应当具有属性(静态特征,比如一个班级,一个专业,一个教室)和行为(动态特征,例如:学习,开会,体育比赛等)两个要素。 对象是由一组属性和一组行为构成的。
类(class):就是对象的类型,代表了某一批对象的共同特性和特征。类是对象的抽象,而对象是类的具体实例。

2.1 类的引入

在C语言中我们定义一个结构体是这样定义的:

struct Student
{
  int _age;
  char* _Gender;
  char* _Name;
};
int main()
{
  struct S;
  return 0;
}

我们都知道,在C中,“数据”和“处理数据的操作(函数)”是分开的,语言本身并没有支持“数据和函数”之间的关联性。那么,如果我们要在某种特定情况下让数据和函数有一定的关联,这个时候我们应该怎么处理呢?
先看一个例子:

这里写图片描述 

很明显,编译器报错了,还很多。也就是说在C语言当中是不允许在结构体中定义函数的,那么在C++中是不是也是这样呢?

这里写图片描述 

通过对比,我们明显的发现C++是可以做到我们想要将数据和函数产生一定关系的操作的。
为了区别C语言中结构体的定义struct,在C++中我们常用class来代替struct。

2.2 类的定义

#include <iostream>
using namespace std;
class Student
{
public:
  char* _name;
private:
  char* _Gender;
  int _age;
};
int main()
{
  class S;
  return 0;
}

class是C++中定义类的关键字,Student为类的名字,{}中为类的主体,和结构体类似,在{}后面跟的是;。类中的元素称为类的成员,类中的数据称为类的属性或者类的成员变量,类中的函数称为类的成员函数。

类的定义通常有两种方式:

1、将类的声明和定义都放在类体中za
2、将类的声明放在.h头文件中,定义放在.cpp文件中

在代码中我们看到了private 和 public两个没有见过的东西,接下来我们讲一讲C++的三大特性:继承 封装 多态
封装:隐藏对象的属性和现实细节,仅对外公开接口和对象进行交互,将数据和操作数据的方法有机结合。

这里的public 和 private是C++中的访问限定符,访问限定符有三个:public (公有) protected(保护) private(私有)。在下面的博文中我们会用到这些限定符,具体情况后续文章再作介绍,这里读者只需了解就可。

这里做一些说明:

1、public成员在类外可以直接访问
2、protected和private成员在类外不能够直接访问,在这里我们简单的把他们看成一样的。(二者区别就是基类private成员在派生类中是不能被访问的,如果基类成员不想再类外被访问,但需要在派生类中能访问,就定义为protected,后面博文更到派生类时我们会再来把这两个东西拿出来再讲一讲)
3、他们的作用域从该访问限定符出现的位置开始直到下一个访问限定符出现截止
4、class的默认访问权限是private,而struct默认访问权限是public(因为struct要兼容C的特点)

那么我们如何在类外访问一个类中的私有成员变量呢?(面试题)

方法一:在类中添加一个共有的方法

这里写图片描述 

我们可以看到在public中定义的公有函数可以在类外访问私有成员变量,这是访问的一种方法。

类的作用域

在C语言阶段我们知道变量的作用域只有局部域和全局域两种,而在C++作用域则分为了局部域、全局域、命名空间域和类域四种,前面几种域相信我们都已将熟悉过,这里我讲一下类域。

类定义了一个新的作用域,类的所有成员都必须处在类的作用域中。形参表和函数体处于类的作用域中。在类体外定义成员,需要用 :: 作用域解析符指明成员属于哪一个类域。在类的作用域外,只能通过对象(或指针)借助成员访问操作符 . 和->来访问类成员,跟在访问操作符后面的名字必须在相关联的类的作用域中。

下面我们看看在代码中如何实现上述的意思:

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
using namespace std;

namespace N1  //命名空间域
{
  int a = 10;
}

int a = 20;   //全局域

void FunTest()
{
  cout << "FunTest()" << endl;
}
class Test
{
public:
  void SetA(int a)
  {
    a = a;      //存在歧义,是变量给变量赋值呢还是形参给变量赋值,或者变量给形参赋值呢?同学们不要这样写 
    //实际证明,这里的赋值是形参给形参赋值,结果并没有改变变量的值,结果出了函数作用域自然是一个随机值 正确写法: **_a = a;**
  }
  void PrintA()
  {
    cout << a << endl;  //cout << _a <<endl;
  }
private:
  int a;         //类域  正确写法:int _a;
};
int main()
{
  int a = 40;       //局部域
  Test t;
  t.SetA(30);
  cout << N1::a << endl;   //打印命名空间域的值
  cout << ::a << endl;    //打印全局域的值
  cout << a << endl;     //打印局部域的值
  t.PrintA();         //打印类域的值
  return 0;
}

看完这串代码不知道各位有没有什么疑惑呢?
这里附上这段代码的结果:

这里写图片描述 

我们本应该想让编译器给我们打印出10 20 30 40这几个值,结果最后却给了我们一个乱码,这是为什么呢?
细心的同学应该发现了,在这里我们定义的所有的变量都是用的同一个名称a,这样的编码习惯是极不好的,容易引起误会,上面就给了一个反例。

在使用一个变量时,我们应当注意必须要先声明后使用

2.2 类的对象的模型

类对象模型:类中各成员在内存中的布局形式。
下面我们以代码形式举例说明一下:

class Student
{
public: 
  void SetStudentInfo(char* name, char* gender, int age)
  {
    strcpy(_name, name);  
    strcpy(_gender, gender);
    _age = age;      
  }
  void PrintInfo()
  {
    cout << _name << " " << _gender << " " << _age << endl;
  }
private:
  char _name[20];
  char _gender[3];
  int _age;
};
int main()
{
  Student s1, s2;
  cout << sizeof(s1) << endl;
  s1.SetStudentInfo("鸣人", "男", 14);
  s2.SetStudentInfo("佐助", "男", 14);
  return 0;
}

既然一个类有那么多成员和不同的对象,那么它到底是怎么来存储的呢?

下面给出三种假设:

假设一

这里写图片描述

假设二:

这里写图片描述

那就剩最后一种假设了:

显然。这种假设才是成立的。

这里写图片描述

提一个问题:C++是怎么计算一个类的大小的?

我们在程序中验证一下:

class A1
{
public:
  void f1()
  {}
  void f2()
  {}
}; 

class A2
{
public:
  void f1()
  {}
private:
  int a = 10;
};
class A3
{};

int main()
{
  cout << sizeof(A1) << endl;  //1
  cout << sizeof(A2) << endl;  //4
  cout << sizeof(A3) << endl;  //1
  return 0;
}

这里写图片描述

可以看到在有成员变量的时候得到的值是4,而一个空类和只有成员函数的时候它的值是1,难道是成员函数的大小?
显然是不对的,前面我们说过,成员函数是不占大小的,因此我们可以得出结论:一个类的大小,实际上就是该类中非静态成员变量之和,当然也要注意内存对齐,空类大小值为一(在Linux和VS下面是 1)
为什么是一呢?
就是为了区分不同的对象,设计 1 是为了 节省空间

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多