C++丨8丨9丨类 Class

本文详细介绍了C++中的类和对象特性,包括成员函数定义、存储方式、对象访问、构造函数(默认构造、无参、带参及默认参数)、析构函数、对象数组和指针,以及构造函数、析构函数的调用顺序、赋值和复制,静态成员和友元的概念。
摘要由CSDN通过智能技术生成

目录

C++丨8丨类和对象的特性

在类外定义成员函数

内置成员函数 inline

成员函数的存储方式

对象成员的引用

C++丨9丨怎样使用类和对象

构造函数(construction)

构造函数分为几种:

(1)系统默认的构造函数(默认构造函数)

(2)不带参数的构造函数

(3)带参数的构造函数(带缺省值的构造函数)

(4)使用默认参数的构造函数⭐

用参数初始化表对数据成员进行初始化

使用无参构造函数

析构函数(destructor)

调用构造函数和析构函数的顺序

对象数组

对象指针

(1)指向对象的指针

(2)指向对象数据成员的指针

(3)指向对象成员函数的指针

this指针///

共同数据的保护

常对象 const

mutable///

常对象成员///

对象的赋值和复制

对象的赋值///

对象的复制

拷贝构造函数(copy constructor)

静态成员 static

静态数据成员

静态成员函数///

友元 friend ///

友元函数 ///

(1)将普通函数声明为友元函数 ///

(2)友元成员函数 ///

友元类 ///

类模板 template ///


C++丨8丨类和对象的特性

面向对象程序设计的4个主要特点:抽象、封装、继承、多态性

类的成员访问权限有3种:

public(公用)、protected(保护)、private(私有)。没有static(静态)!


在类外定义成员函数

在函数名前面加上【::】作用域限定符 即可!(不加的话就是全局函数啦

class Student {
public:
    void F(); // 函数原型声明
private:
    string name;
};

void Student::F() {

}

PS:以上形式是一种书写类的好习惯! 要学习!(先写public函数成员,然后写private数据成员,然后在类外实现函数。)


内置成员函数 inline

如果在类体中定义的成员函数中不包括循环等控制结构,C++系统自动地对它们作为内置(inline)函数来处理。

类内定义的成员函数,可以省略inline,因为这些成员函数已被隐含地指定为内置函数。(好像根本没有人会写的样子...

class Student {
public:
    void F() {}
    // 等价于 inline void F() {}
private:
    string name;
};

 意思是说类外定义的时候不会被默认为内置函数哦!想要就自己写。


成员函数的存储方式

同一个类的成员函数只存一份!(即一堆对象跑去类里用同一个函数。)不过数据是单独存放在各个对象里的!对象的存储空间里只有数据,没有函数!


对象成员的引用

访问对象中成员的3种方式:(只能访问public公有成员!)

(1)通过对象名和成员运算符访问对象中的成员

对象名.成员名
Time t;
t.hour;
t.F();

(2)通过指向对象的指针访问对象中的成员

Time t;
Time* p = &t; // p指向对象t
cout << p->hour;

(3)通过对象的引用访问对象中的成员

Time t1; // 定义对象t1
Time& t2 = t1; // 定义Time类引用t2,并使之初始化为t1
cout << t2.hour;

C++丨9丨怎样使用类和对象

:你怎么好像什么都不会的样子。


构造函数(construction)

class Box {
    Box() {} // 构造函数
};

(1)用来处理对象的初始化。(因为你不可以在声明数据成员的时候给它们赋值。)

(2)构造函数是一种特殊的成员函数,它与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。(只能执行一次。系统自动调用,不可以被用户调用)

(3)构造函数的功能是由用户定义的,用户根据初始化的要求设计函数体和函数参数,可以是一个,也可以是多个。

(4)构造函数可以重载!(函数名相同,不会返回任何类型,也不可以是void类型,参数类型/个数可不同)。

(5)构造函数的名字必须与类名相同!!

构造函数分为几种:

class Date
{
    int year;
    int month;
    int day;
(1)系统默认的构造函数(默认构造函数)
    // 系统默认的构造函数
    Date() { } // 即 Date() = default
    // 一旦你写了一个别的构造函数,系统就不生成这个了!
(2)不带参数的构造函数
    // 不带参数的构造函数
    Date() {
        year = 1999;
        month = 9;
        day = 9;
    }
(3)带参数的构造函数(带缺省值的构造函数)
    // 带参数的构造函数
    Date(int y, int m) {
        year = y;
        month = m;
        day = 9;
    }
    // 带参数的构造函数
    Date(int y, int m, int d) {
        year = y;
        month = m;
        day = d;
    }
    // 可以对其中某一些数据指定默认值,但不能全部都有默认值!!不然就是类型(4)
    // 这种方式下,有2或3个参数都可以执行。
    Date(int y, int m, int d = 9) {
        year = y;
        month = m;
        day = d;
    }

PS:这里构造函数一多很容易不小心出现歧义。要具体情况具体分析!

大概思路就是,假设你在构造对象的时候输入了n个参数,其中能运行n个参数的情况的构造函数(一个一个判断下去)有几个。如果只有一个就没有歧义!

PS:在声明构造函数的时候指定了默认值的话,定义(实现)函数的时候就不要再写一遍了!会报错。可参见《Ruru的报错日常》里的 [Error]005 TT

Box(int h = 10, int w = 10); // 声明

Box::Box(int h, int w) {...}; // 定义。不要再写 =10 啦!

(4)使用默认参数的构造函数⭐
    // 使用默认参数的构造函数
    // 输入0、1、2、3个参数时都可以执行!
    Date(int y = 1999, int m = 9, int d = 9) {
        year = y;
        month = m;
        day = d;
    }

⭐PS:有了这种全部是默认参数的构造函数后,就不能再定义重载构造函数了!!

因为此时你即使只写 Date(2000); 也是可·以·运·行的!效果为将y由1999替换为2000。假设你还定义了一个只有一个参数的构造函数,此时就会有·歧·义!!所以!

⭐反正就是无论你输入几个参数都可以执行,所以有其它构造函数时必然会出现歧义。(啊!受不了我的表达能力了!

⭐PS:以下两种都属于默认构造函数!不能同时存在!

// 1. 没有参数
    Date();
// 2. 所有参数均有默认值
    Date(int y = 1999, int m = 9, int d = 9);

默认构造函数:总的来说就是调用构造函数时,不必给出实参的构造函数!


用参数初始化表对数据成员进行初始化

这种方法不在函数体内对数据成员初始化,而是在函数首部实现。

    // 用参数初始化表对数据成员初始化
    Date(int y, int m, int d) :year(y), month(m), day(d) {
        // 构造函数体(可写可不写)
    }

    Date date(1999, 9, 9);

};

使用无参构造函数

Box box1;    (√)
Box box2();  (x)

另:构造函数不可以被显式调用,所以请不要出现 调用 Box() 这种神奇的形式! 


析构函数(destructor)

class Box {
    Box() {}  // 构造函数
    ~Box() {} // 析构函数
};

(1)析构函数的名字也是固定的:~类名

( ~ 位取反运算符。So:析构函数是与构造函数作用相反的函数。(好好嗑! 

(2)当对象的生命期结束时,会自动执行析构函数。

(3)我们的口号是!——清除对象,释放内存。(析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作)

(4)析构函数没有返回值、函数类型、函数参数。不可以被重载。一个类可以有多个构造函数,但是只能有一个析构函数。(等等,突然不那么好嗑了....

(5)它可以被用来执行 用户希望在最后一次使用对象之后所执行的任何操作。例如输出相关的信息。(嗯??什么遗言(。


调用构造函数和析构函数的顺序

省流:像洋葱!(or先穿袜子再穿鞋子,然后脱鞋子脱袜子.... or搭积木 or something...

总之就是先构造的后析构。(这里纯粹是代码执行顺序。不关继承or套娃的事哦。)


对象数组

Box box[3] = {
    Box(1, 2),
    Box(3, 4),
    Box(5, 6);
};

对象指针

(1)指向对象的指针

和结构体指针类似!传送门:C++丨7丨用户自定义数据类型_巧克力Pie的博客-CSDN博客

(2)指向对象数据成员的指针

p1 = &t1.hour; // p1指向t1.hour
cout << *p1;   // 输出t1.hour的值

(3)指向对象成员函数的指针

重温普通函数的指针变量定义方法:

void(*p)(); // p是指向void型函数的指针变量
p = fun;    // 将fun函数的入口地址赋给指针变量p。p指向fun
(*p)();     // 调用fun函数

定义一个指向对象成员函数的指针变量的方法: 

p1 = t1.get_time; (x) 报错!!
void (Time::* p2)(); // 定义p2为指向Time类中公有成员函数的指针变量
// (Time::* p2)两边的()不可以省略!否则p2会先和后面的()结合
void Time::* p3(); 相当于:
void(Time::* (p3())); // 返回值为void型指针的函数

p2 = &Time::get_time; (√)

this指针///

......


共同数据的保护

常对象 const

作用:数据共享+防止被修改

(1)常对象必须要有初值。

Time const t1( 12, 0, 0); // 定义t1为常对象
const Time t1( 12, 0, 0); // 定义t1为常对象

两种写法都可以!

(2)初始化之后不能再改变。(数据共享+防止被修改)

(3)如果一个对象被声明为常对象,则通过该对象只能调用它的常成员函数(内置保护机制:不可以修改数据的值),而不能调用该对象的普通成员函数(除了由系统自动调用的隐式的构造函数和析构函数)。

将函数成名为const的方法:

void get_time() const; // 将函数声明为const

mutable///

......

常对象成员///


对象的赋值和复制

对象的赋值///

对象的复制

(1)操作系统生成的:浅复制

类名 对象2(对象1);
Box box2(box1);
// The copy constructor definition
Box::Box(const Box& b) {
    height = b.height;
    width = b.width;
    length = b.length;
}

(2)自定义复制构造函数:深复制。

拷贝构造函数(copy constructor)

(1)类在我们没有定义拷贝构造函数的时候,会默认定义默认拷贝构造函数,也就是说可以直接用同类型的类间可以相互赋值、初始化。

(2)拷贝分为:浅拷贝、深拷贝。


静态成员 static

静态数据成员

(1)共有数据 / 数据共享!如果希望各对象中的数据成员的值是一样的,可以把它定义为静态数据成员!

(2)静态数据成员是类的成员,而不是对象的成员。

(3)它的数据存在单独的地方。不和对象成员一起。

(4)静态数据成员可以初始化,但只能在类外进行初始化!!

数据类型 类名::静态数据成员名 = 初值;
// PS:在类内声明时加static即可。初始化的时候不要加!
class Box {
    static int height;
};

int Box::height = 10;

(5)不能用参数初始化表对静态数据成员初始化。

(6)在类外可以通过对象名访问公共的静态数据成员,也可以通过类名引用静态数据成员。

Box box;
box.height;  (√) 通过对象名访问公共的静态数据成员
Box::height; (√) 通过类名引用静态数据成员

静态成员函数///

(1)无this指针!

(2)只能访问静态数据成员。(不然0个对象的时候其它值都没有数据)


友元 friend ///

友元函数 ///

书写:在类外定义直接写(不用加friend),在类内声明要加上friend前缀。

⭐并且!类外要再声明一次不带friend的。(而且要在类声明之后。)

(1)将普通函数声明为友元函数 ///

不用写xx::

(2)友元成员函数 ///

要写xx::

友元类 ///


类模板 template ///

模板是一组类(或函数)的抽象。

我们来看一下对比!

普通类和它的函数:

class MyClass
{
	int data;
public:
    MyClass(); // 构造函数
}
// 构造函数
MyClass::MyClass()
{
    //
}

写成类模板和它的函数:

类模板:前面要加  template <class Type> ,需要用到 Type 的地方就写 Type

template <class Type> // 一般也会写成T
class MyClass
{
	Type data; // Type类型的数据
public:
    MyClass(); // 构造函数
}

函数:前面要加  template <class Type> ,类名后面要加 <Type>

// 构造函数
template <class Type>
MyClass<Type>::MyClass()
{
    //
}

 在主函数中使用:

int main()
{
    MyClass<student> stu(20);
}

参考:C++编程——类模板_Stephen_Tao的博客-CSDN博客


参考:

类的构造函数-CSDN博客

C++中的五种构造函数_c++构造函数有几种类型_TABE_的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值