一.目录

  1.对象的相关知识

  2.类的定义

  3.类的实例化

  4.类对象模型

  5.模拟实现offsetof宏

  6.this指针

二.正文

1.对象的相关知识

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

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

  对象:任何一个对象都应该具有两个要素,即属性和行为,对象是由一组属性和行为构成的。如现实生活中的手机就是一个对象,它的属性就是生产厂家,配置,颜色等等,行为就是它的功能。在一个系统中的多个对象之间通过一定的渠道相互联系,如下图:

  在C++中,每个对象都是由数据和函数(即操作代码)这两部分构成的,数据体现了属性,函数就是对数据进行操作的,以便实现某些功能。(通过三角形这个对象的边长,通过数学公式(函数)计算出三角形的面积(实现功能)。

2.类的定义

  C语言中,结构体中只能定义变量,在C++中,结构体中不仅可以定义变量,还可以定义函数。

  C++中的类就是将对象的属性和行为结合在一块,通过访问权限将接口提供给外部用户使用,也就是类的封装属性,用户不需要知道具体的实现方法,进而提高了数据的安全性。

  类的访问限定符:

  public(公有):修饰的成员变量在类外也可以被直接访问

  protected(保护):在类外不能被直接访问

  private(私有):在类外不能被直接访问

  类访问限定符的作用域从该访问限定符出现的位置到下一个访问限定符出现时为止

  注意:在C++中,为了兼容C语言的一些特性,struct的功能和C语言相同,但增加了新的功能,即可以在struct中定义函数,struct和class相同,struct默认成员为public,private不加访问限定符时默认成员为private。

  访问限定符只在编译期间有作用,当数据 映射到内存的时候没有任何访问限定符上的区别

  类的具体实现:

(1)关键字:class

(2)用法:class [classname],{}为类的主体,注意类定义结束后的封号

 class Student
{
void TestFun(){//成员函数
}
int a;//成员变量
};

  类的两种定义方法:

  (1)声明和定义都放在类体中,注意:如果成员函数在类体中,则编译器在编译期间会将类的成员函数当做内联函数展开(不会在Debug版本下展开,而在发布版本中展开)

 class Student
{
public:
int Add(int math, int english){
score=math+english;
cout<<a<<" "<<name<<" "<<endl;
}
public:
int score;
char *name;
};

  (2)在类中申明(放在.h文件中),在类外定义(放在.c文件中)

//Student.h文件
class Student
{
public:
void Add(int math, int english);
void PrintScore();
private:
int score;
}; //Student.c
void Student::Add(int math, int english)
{
score=math + english;
} void Student::PrintScore()
{
cout<<score<<" "<<endl;
}

  注意:这种方法在定义时需要用作用域限定符“::”指定定义的函数属于这个类作用域

3.类的实例化

  定义:用类类型创建对象的过程称之为类的实例化

  解释:一个类可以建立多个对象,定义类的时候操作系统不会为其开辟内存空间,只有当用类类型定义了对象之后对象才会占用物理地址空间,存储类成员变量。打个比方,类就像现实生活中建筑物的建筑图纸,而建筑物就是用图纸建的对象,图纸中的的建筑物并不真实存在,但建造(实例化)后的建筑物真实存在于客观世界。

 class Student//类
{
public:
int Add(int math, int english){
score=math+english;
}
public:
char *name;
int score;
int id;
}; int main()
{
Student S1;//对象
S1.Add(,);
cout<<score<<" "<<endl;
return ;
}

4.类对象模型

  (1)如何计算类对象的大小?

  由于类对象具有成员函数和成员变量,那么该如何计算类的大小?这就是下面要讲的。

  (2)类对象的存储方式猜测

  a.如果成员函数和成员变量存储在连续内存空间中,则当一个类的成员变量较多时,而且定义多个对象时,会将里面的成员变量与成员函数都复制一份为其开辟内存,每个对象都会保存一份代码,但是这些对象都共用同一个函数,相同代码保存多次,浪费空间,因此不会为每个对象都复制一份代码。

   b.还有另一种可能情况,如下图所示情况,只保存成员变量,成员函数放在公共的代码段

  c.为了验证上面两种猜测,我们可以通过代码求得类对象的大小

 class Student
{
pupblic:
void PrintScore(){
cout<<score<<" "<<endl;
}
int Add(int math , int english)
{
score=math + english;
}
private:
int score;
} int main()
{
Student S1;
cout<<sizeof(S1)<<" "<<endl;
reutrn ;
}

  通过上述代码可以求得类对象的大小,进而判断出第二种猜测是正确的。即成员函数在公共代码区,不会为每个对象都保存一份公共的代码。类的对象的大小求解方法和C语言中的结构体的大小相同。存在内存对齐的方式。

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

(3)结构体中的内存对齐规则

  a.第一个成员变量在与结构体偏移量为0的地址处

  b.其他成员要对齐到某个数字(对齐数)的整数倍地址处。注意:对齐数=编译器默认的对齐数与该成员变量的较小值(VS中的默认对齐数是8,gcc中的默认编译器是4

  c.结构体的总大小为:最大对齐数(所有变量的类型最大值与编译器默认对齐数取最小)的整数倍处

  d.如果嵌套了结构体,嵌套的结构体对齐到自己最大的整数倍处,结构体的整体的大小就是所有最大对齐数的整数倍(含有嵌套结构体的对齐数)

  e.可以用#pragma peak(n)这个预处理指令设置默认的对齐数为n字节对齐,当然,n应该是2的k次方,不然无法对齐。

  f.通过offsetof这个宏可以求解得某个成员相对于结构体的起始位置的偏移量offsetof(类名,成员名)

5.模拟实现offsetof宏

    #define offsetof(TYPE,MEMBER) (size_t)&((TYPE *)0)->MEMBER)

  这个宏可以根据下图理解:

6.this指针

  (1)在看this指针前先看一个例子:

 class Student
{
public:
void PrintFun(){
cout<<_gender<<" "<<endl;
cout<<_name<<" "<<endl;
cout<<_age<<" "<<endl;
}
void Setstudentinfo(char *name, char* gender, int age)
{
strcpy(_name,name);
strcpy(_gender,gender);
_age=age;
}
private:
char *_name;
char* _gender;
int _age;
}; int main()
{
Student S1;
Student S2;
S1.Setstudentinfo("TOM","male",);
S2.Setstudentinfo("perter","male",);
S1.PrintFun();
S2.PrintFun();
return ;
}

  对于上述两个类,有这样一个问题:

  Student类中有Setstudentinfo和PrintFun两个成员函数,函数体中没有关于不同对象的区分,而且这两个成员函数存储 在代码公共区,那么当S1调用Setstudentinfo函数时,该函数如何知道应该设置S1对象而不是设置S2对象呢?

  C++通过引入this指针解决了这个问题,即:C++编译器给每个“成员函数”增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中的所有成员变量的操作,都是通过this指针去访问。只不过所有的操作对于用户是透明的,即用户不用传递该参数,编译器自动完成。

  (2)this指针的特性

  a.this指针的类型:类类型 * const this

  b.只能在“成员函数”内部使用

  c.this指针本质上其实是一个成员函数的第一个参数,是对象调用成员函数时,将对象的地址传给this指针,所以对象中不存储this指针。

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

  注意:this指针不能作为左值,只能在“成员函数”内部使用。并不是所有的函数的this指针都用ecx寄存器传递,比如不定参数函数的this指针就用eax寄存器函数压栈的方式传递地址。这是因为调用函数时的调用约定不同,有的函数前面加了_thiscall(通过ecx寄存器传递),而有的函数前面加了_cdecl(通过压栈的方式传递)

  this指针可以为空值,但如果是空值,则在成员函数内部不能访问对象的成员变量。如下面的代码:

 class A
{
public:
int Add(int left , int right){
a=left+right;
}
void PrintA(){
cout<<a<<endl;
}
private:
int a;
} int main()
{
A *p=NULL;//定义一个类类型的指针,指向这个A类
p->Add(,);//此时类类型的指针是空值,调用它的成员函数的时候讲NULL复制给this指 针,this指针为NULL,则执行Add函数时因this访问a,所以会报错。
p->PrintfA();
return ;
}

  

C++类和对象(一)&&实现offsetof宏&&this指针的更多相关文章

  1. c++面向过程和面向对象-C++编译器是如何管理类和对象的

    1,c++编译时如何区分对象调用类的方法? C++中的class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类.从计算机的角度,程序依然由数据段(栈区内存)和代 ...

  2. C++编译器是如何管理类和对象的,类的成员函数和成员变量

    C++中的class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类.从计算机的角度,程序依然由数据段(栈区内存)和代码段(代码区内存)构成. #include ...

  3. c++ 吕凤翥 第六章 类和对象(二)

    c++ 吕凤翥 第六章 类和对象(二) 指针   引用  和数组 一:对象指针和对象引用 1.指向类的成员的指针 分为指向成员变量和指向成员函数两种指针 成员变量的格式:     类型说明符  类名: ...

  4. C++ 关于类与对象在虚函数表上唯一性问题 浅析

    [摘要] 非常多教材上都有介绍到虚指针.虚函数与虚函数表.有的说类对象共享一个虚函数表,有的说,一个类对象拥有一个虚函数表.还有的说,不管用户声明了多少个类对象,可是,这个VTABLE虚函数表仅仅有一 ...

  5. C++基础-类和对象

    本文为 C++ 学习笔记,参考<Sams Teach Yourself C++ in One Hour a Day>第 8 版.<C++ Primer>第 5 版.<代码 ...

  6. C++_基础_类和对象

    内容: (1)引用 (2)类型转换 (3)C++社区给C程序员的建议 (4)面向对象编程的概念 (5)类和对象 (6)构造函数 (7)初始化列表及其必要性 1.引用1.1 指针和引用的使用说明(1)指 ...

  7. C++类与对象(05)

    类是具有惟一标识符的实体:在类中声明的任何成员不能使用extern.auto和register关键字进行修饰:类中声明的变量属于该类,在某些情况下,变量也可以被该类的不同实例所共享. 访问权限用于控制 ...

  8. php 类和对象

    ⾯向对象是⼀种编程范式,它将对象作为程序的基本单元,将程序和数据封装起来, 以此来提⾼程序的重⽤性.灵活性和可扩展性. ⽬前很多语⾔都⽀持⾯向对象编程,既然对象对象是⼀种范式,其实这就和具体的编程语⾔ ...

  9. C++_类和对象

    类和对象 OOP第二课 1 类的构成 1.1 从结构到类 1.2 类的构成 2 成员函数的声明 2.1 普通成员函数形式 2.2 将成员函数以内联函数的形式进行说明 3 对象的定义和使用 3.1 对象 ...

随机推荐

  1. 2019.01.20 bzoj3999: [TJOI2015]旅游(树链剖分)

    传送门 树链剖分菜题. 题意不清差评. 题意简述(保证清晰):给一棵带权的树,每次从aaa走到bbb,在走过的路径上任意找两个点,求后访问的点与先访问的点点权差的最大值. 思路: 考虑暴力:维护路径的 ...

  2. 2018.11.02 NOIP模拟 飞越行星带(最小生成树/二分+并查集)

    传送门 发现题目要求的就是从下到上的瓶颈路. 画个图出来发现跟去年noipnoipnoip提高组的奶酪差不多. 于是可以二分宽度+并查集检验,或者直接求瓶颈. 代码

  3. warning: this decimal constant is unsigned only in ISO C90问题的处理及理解

    参考:https://blog.csdn.net/duguduchong/article/details/7709482 https://bbs.csdn.net/topics/391892978?p ...

  4. VSS + Eclipse 管理源码

  5. jpa命名规范

      Keyword Sample JPQL snippet And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname ...

  6. pyppeteer使用笔记

    pyppeteer -- python版本的puppeteer,一个强大的chronium headless浏览器API 最近搞天猫用了一波儿,记录一下. 先上文档: https://miyakogi ...

  7. java基础-day7

    第07天 面向对象基础 今日内容介绍 u 面向对象概述 u 面向对象特性之封装 u 面向对象之构造方法 u 类名作为形参和返回值案例 第1章   面向对象概述 1.1      面向对象思想 1.1. ...

  8. STL容器之一vector

    STL中最简单也是最有用的容器之一是vector<T>类模板,称为向量容器,是序列类型容器中的一种. 1.vector<T> 对象的基本用法(1)声明:vector<ty ...

  9. lowbit(x)

    int Lowbit(int x) { return x&(-x); } lowbit当中x,-x,补码,反码,傻傻分不清楚.我们先看看两个二进制数相减的问题 两个二进制数相减的相关问题 两个 ...

  10. hdu 5072 两两(不)互质个数逆向+容斥

    http://acm.hdu.edu.cn/showproblem.php?pid=5072 求n个不同的数(<=1e5)中有多少组三元组(a, b, c)两两不互质或者两两互质. 逆向求解,把 ...