类和对象(1)

面相过程和面向对象区别

c语言就是面向过程的语言,即关注的是过程的求解,我们利用函数逐步解决问题。

c++是面向对象的语言。将一件事情拆分成不同的对象,靠的是对象之间的关系完成。

类的引入

c++中结构体不仅可以定义变量,还能定义函数,而在c语言中结构体只能定义变量。现在以c++方式实现,发现struct也可以定义函数。

类的定义

class ClassName
{
  //由成员函数和成员变量组成
};

类体中的内容称为类的成员,类的变量称为类的属性或成员变量,类中的函数称为类的方法或成员函数。

类的定义方式

1类的声明和定义全部放在类体当中,需要注意的是,如果函数在类中定义,编译器可能会将其当作内联函数处理。

class Person
{
  public:
    void ShowPeoInfo()
    {
       cout<<_name<<"-"<<_age<<"-"<<_sex<<endl;
    }
 public:
    char* _name;
    char* _sex;
    int age;
};

    

2类的声明放在头文件中,类的函数放在源文件当中。注意:成员函数之前需要加类名::

声明放在person.h文件当中 

class Person
{
  public:
    void ShowPeoInfo();
  public:
    char* _name;
    char* _sex;
    int age;
}:

    

person.cpp

注意:成员函数前需要加上类名

#inlcude<person.h>
void Person::ShowPeoInfo()
{
   cout<<_name<<"-"<<_age<<"-"<<_sex<<endl;
}

成员命名规则的建议:

Class Date
{
  public:
     void Init()
     {
        year=year;
      }
  private:
     int year;
};

这里有一个问题,那就是这里的year到底是成员变量还是函数形参?

我们应该用如下方式命名变量:

Class Date
{
  public:
     void Init()
     {
        year=_year;
      }
  private:
     int _year;
};

类的访问限定符以及封装

c++用类的方法将对象的属性和方法放在一起,通过选择性地将接口提供给外部使用来实现封装。

访问限定符的作用域是从这个访问限定符开始到下一个访问限定符

class的默认访问权限为private,struct的默认访问权限为public。

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

封装

面向对象的三大特点:封装,继承,多态

封装:将数据以及操作数据的方法有机结合,隐藏对象的属性和实现细节,仅对外提供接口来和对象进行交互。

类的作用域

类定义了一个新的作用域,在类体外定义成员时,需要用域作用限定符::指明成员属于哪个类域。

类的实例化

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

1 类是对对象进行描述的,定义出一个类并没有实际的空间储存它

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

int main()
{
  Person._age=100;
  return 0;
}

// 编译失败:error C2059: 语法错误:“.”

Person类是没有空间的,只有person实例化出的对象才有物理空间。

类对象模型 

类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?

结论:一个类的大小,就是成员变量之和,注意内存对齐。

空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

this指针

class Date
{ 
public:
 void Init(int year, int month, int day)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 void Print()
 {
 cout <<_year<< "-" <<_month << "-"<< _day <<endl;
 }
private:
 int _year;     // 年
 int _month;    // 月
 int _day;      // 日
};
int main()
{
 Date d1, d2;
 d1.Init(2022,1,11);
 d2.Init(2022, 1, 12);
 d1.Print();
 d2.Print();
 return 0;
}

这里有一个问题,Date中有两个成员函数,Init和Print,函数体中没有不同对象的区分,那么当d1调用Init函数时候,如何知道不是d2而是d1对象调用的呢?

c++中通过引入this指针来解决这个问题:

c++给每个非静态的函数一个this指针,通过指向当前对象(函数运行时调用的对象),所有成员变量的操作,都是通过指针去访问。只不过所有操作都是编译器自己完成的,不需要用户来操作。

例如:d1.Init(&d1,2022,1,11);会将地址传递过去。

前面还有一个指针:

void Init(int year, int month, int day)
 {
 this->_year = year;
 this->_month = month;
 this->_day = day;
 }

特性

1 类类型*const,不能给this指针赋值(const*修饰的是指向的对象,*const修饰的是指针本身)

2 本质上是成员函数的形参,当对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针

3 this指针是成员函数的第一个形参,一般由编译器ecx自动传递

比如Date类的Init类的真实原型为:

void Init(Date* const this,int year,int month,int day)

this指针存在内存的哪个区域?

E一定不对,因为刚刚我们算对象大小的时候就没有算this指针。

选A

下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行

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

空指针不可能是编译错误,最多是运行崩溃。这个题是A正常运行,为什么呢?这里不是有空指针吗?

编译器编译以后,都会转换为汇编指令。 p->Print();这句代码转换成的编译指令是call 地址。地址不在p对象里(成员函数的指针不存到对象里面)。为什么需要p调用呢?因为Print()是内域,是成员函数,编译的时候就知道去Class A里面找,这里不需要取地址,因为p就是对象的地址//mov ecx p(ecx存的是this指针)。把p传给了this,this是空指针。cout<<this<endl;空指针没有解引用,并不会报错。如果是p->_a=1;这个时候成员变量被引用,就崩溃了。

 下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行

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

这个题目的答案是B。相当于解引用了。

C语言和C++实现Stack的对比

c语言

c语言在实现函数时,操作函数有以下特性:

1每个函数的第一个参数都是Stack*

2函数中必须要对第一个参数检测,第一个参数是NULL

3函数都要通过Stack*操作栈

4 调用时必须穿Stack结构体变量的地址

结构体内只能存放数据的结构,操作数据的方法不能放在结构体当中,即数据和数据操作的方式是分开的。

c++

c++中通过类,可以将数据以及操作数据的方法完美结合,通过访问权限可以控制那些方法在类外的函数可以被调用,即封装。C++中 Stack *是编译器自己维护的,而c语言中是需要用户进行维护的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值