C++复习 - 类和对象

声明: 本篇博客的学习途径主要为以下网站和课堂讲解,发博客目的仅为学习使用
http://c.biancheng.net/cplus/

类只是一个模板(Template),是一个设计图,编译后不占用内存空间,所以在定义类时不能对成员变量进行初始化,因为没有地方存储数据。
只有在创建对象,(盖楼)以后才会给成员变量分配内存,这个时候就可以赋值了。

成员变量大都以m_开头,这是约定成俗的写法,不是语法规定的内容。以m_开头既可以一眼看出这是成员变量,又可以和成员函数中的形参名字区分开。


类外实例化对象:对象与new

对象: 实例化的对象
栈上创建出来的对象有名字,比如stu。
使用 new 在堆上创建出来的对象是匿名的,没法直接使用,必须要用一个指针指向它,再借助指针来访问它的成员变量或成员函数。

Student stu;
Student *pStu = &stu;

Student *pStu = new Student;

借助的这个指针*pstu 通过->来访问对象的变量和函数与结构体指针类似


类内:成员函数的声明与定义

成员函数必须先在类体中作原型声明,然后在类外定义,也就是说类体的位置应在函数定义之前。
定义在类外,需要用到域解析符::

class Student{
public:
    //成员变量
    char *name;
    int age;
    float score;

    //成员函数
    void say();  //函数声明
};

//函数定义
void Student::say(){
    cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl;
}
  • 为什么函数声明在类内,定义在类外呢?
    因为类体内部定义的函数默认就是内联函数
    内联函数一般不是我们所期望的,它会将函数调用处用函数体替代,所以我建议在类体内部对成员函数作声明
    ∴ 没有必要在类外把定义在类外的函数添加inline关键字。

类内: 成员访问权限

成员访问限定符:public , protected , private
只在内部类使用的变量设为private
允许外部调用的函数 设为public
(常通过public的set get来修改和获得类内的private变量)
只允许子类访问的变量设为:protect

所谓封装,是指尽量隐藏类的内部实现,只向用户提供有用的成员函数。


C++对象的内存模型

编译器会将成员变量和成员函数分开存储:分别为每个对象的成员变量分配内存,但是所有对象都共享同一段函数代码
在这里插入图片描述


C++函数的编译

C语言编译: 增加下划线_ 例如,func() 编译后为 func() 或 _func()。
C++编译:Name Mangling 的算法,根据它所在的命名空间、它所属的类、以及它的参数等信息进行重新命名,该过程可逆,以?开始。


成员函数的调用

前清概要:成员函数最终被编译成与对象无关的全局函数
【问题1】如果成员函数中使用到了成员变量该怎么办呢?
指针,通过传递对象指针就完成了成员函数和成员变量的关联,C++规定,编译成员函数时要额外添加一个参数,把当前对象的指针传递进去,通过指针来访问成员变量。

void Demo::display(){
    cout<<a<<endl;
    cout<<b<<endl;
}

编译后代码:

void new_function_name(Demo * const p){
    //通过指针p来访问a、b
    cout<<p->a<<endl;
    cout<<p->b<<endl;
}

C++构造函数(Constructor)

构造函数:创建对象时,自动执行

  • 构造函数必须是 public 属性的,否则创建对象时无法调用

构造函数没有返回值,因为没有变量来接收返回值,即使有也毫无用处,这意味着:

  • 不管是声明还是定义,构造函数名前面都不能出现返回值类型,即使是 void 也不允许;
  • 函数体中没有 return 语句。
构造 函数的重载

创建对象时根据传递的实参来判断调用哪一个构造函数
构造函数的调用是强制性的一旦在类中定义了构造函数,那么创建对象时就必要调用,不调用是错误的。


默认构造函数

如果用户自己没有定义构造函数,那么编译器会自动生成一个默认的构造函数

Student(){}

一个类必须有构造函数,要么用户自己定义,要么编译器自动生成。一旦用户自己定义了构造函数,编译器都不再自动生成

注意: 调用没有参数的构造函数也可以省略括号

Student stu();
Student stu;
Student *pstu = new Student();
Student *pstu = new Student;

构造 函数初始化列表

函数初始化列表,有两个作用:

  • 目的是为了书写方便,简单明了
  • 初始化 const 成员变量
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){
    //TODO:
}

函数首部与函数体之间添加了一个冒号:,后面紧跟

:m_name(name), m_age(age), m_score(score)

语句
相当于函数体内部的

m_name = name; m_age = age; m_score = score;

注意,成员变量的初始化顺序与初始化列表中列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关


初始化 const 成员变量

class VLA{
private:
    const int m_len;  // const修饰的变量
    int *m_arr;
public:
    VLA(int len);
};

//必须使用初始化列表来初始化 m_len
VLA::VLA(int len): m_len(len){
    m_arr = new int[len];
}

C++析构函数(Destructor)

析构函数(Destructor)也是一种特殊的成员函数,没有返回值,而是在销毁对象时自动执行
如果用户没有定义,编译器会自动生成一个默认的析构函数。
注意: 析构函数不能被重载!因此一个类只能有一个析构函数

生的各有不同,死的殊途同归

VLA::~VLA(){
    delete[] m_arr;  //释放内存
}

C++ 中的 new 和 delete 分别用来分配和释放内存,它们与C语言中 malloc()、free() 最大的一个不同之处在于:用 new 分配内存时会调用构造函数,用 delete 释放内存时会调用析构函数


析构函数的执行时机

析构函数在对象被销毁时调用->对象的销毁时机与它所在的内存区域有关

  • 全局对象,程序在结束执行时会调用这些对象的析构函数
  • 局部对象,位于栈区,函数执行结束时会调用这些对象的析构函数。
  • 堆区创建的对象,通过 delete 删除时才会调用析构函数;如果没有 delete,析构函数就不会被执行。

C++对象数组(数组的每个元素都是对象)

对象数组中的每个元素都需要用构造函数初始化。

C++成员对象和封闭类

  • 成员对象: 一个类的成员变量如果是另一个类的对象,就称之为“成员对象”。
    (用对象,作为属性)
  • 封闭类: 包含成员对象的类叫封闭类(enclosed class)。
    (包含对象的类,叫封闭类)

成员对象的初始化

创建封闭类的对象时,它包含的成员对象也需要被创建,这就会引发成员对象构造函数的调用。
【问题1】如何让编译器知道,成员对象到底是用哪个构造函数初始化的呢?

借助封闭类构造函数的初始化列表。

类名::构造函数名(参数表): 成员变量1(参数表), 成员变量2(参数表), ...
{
    //TODO:
}
  • 对于普通的属性,“参数表”中是初始值,在调用构造函数时,会把这个初始值直接赋给成员变量。
  • 对于成员对象,“参数表”中存放的是构造函数的参数,它可能是一个值,也可能是多个值,它指明了该成员对象如何被初始化。

成员对象的消亡

封闭类对象生成时,先执行所有成员对象的构造函数,然后才执行封闭类自己的构造函数

封闭类对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数


this指针

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值