C++面向对象基础

目录

一.函数

1.内联函数

2.函数重载

3.哑元函数

二.类和对象

[2.1 类的定义](#2.1 类的定义)

[2.2 创建对象](#2.2 创建对象)

[三. 封装(重点)](#三. 封装(重点))

[四. 构造函数 constructor(重点)](#四. 构造函数 constructor(重点))

[4.1 基础使用](#4.1 基础使用)

[4.2 构造初始化列表](#4.2 构造初始化列表)

[4.3 构造函数的调用方式(掌握)](#4.3 构造函数的调用方式(掌握))

[4.4 拷贝构造函数](#4.4 拷贝构造函数)

[4.4.1 概念](#4.4.1 概念)

4.4.2浅拷贝

4.4.3深拷贝

[五. 析构函数 destructor(重点)](#五. 析构函数 destructor(重点))


一.函数

1.内联函数

内涵函数的正确使用可以提升程序的执行效率。内联函数用于取代C语言中的宏定义函数。内联函数在编译的时候,直接把函数展开到主函数中进行编译,在运行期间减少调用的开销。
通常将具有以下性质的函数写为内联函数:
● 代码长度在5行以内
● 不包含复杂的控制语句
● 频繁的被调用

cpp 复制代码
#include <iostream>
using namespace std;
inline void print_string(string str)
{
    cout << str << endl;
}
int main()
{
    print_string("helloworld");

    return 0;
}

关键字:inline
后续学习的成员函数默认添加inline修饰。
我们手动添加上inline关键字,将函数声明为内联函数,。但是这个不是我们可以决定的,编译器有自己的判断准则。我们只是非常卑微的给编译器提一个建议,具体是否变为内联函数,还是编译器自己决定的。

2.函数重载

C++中允许多个函数使用同一个名称,这种用法就是函数重载。函数重载要求函数名称相同,但是参数不同(类型或者数量不同或者前后顺序不同)。与返回值类型等其他因素无关。

cs 复制代码
#include <iostream>
using namespace std;

void print_show(int i)
{
    cout << "调用int重载:" << i << endl;
}

// 错误 名称相同,参数相同编译器无法区分
//void print_show(int i)
//{
//    cout << "调用int重载:" << i << endl;
//}

void print_show(string str)
{
    cout << "调用了string重载:" << str << endl;
}


//int print_show(const int i)
//{
//    cout << "调用int2重载:" << i << endl;
//}


//void print_show(float i)
//{
//    cout << "调用float重载:" << i << endl;
//}

void print_show(double i)
{
    cout << "调用double重载:" << i << endl;
}


int main()
{
    print_show(2.3);
    return 0;
}

3.哑元函数

函数的参数只有类型,没有名称,有这样参数的函数就是哑元函数。

cpp 复制代码
#include <iostream>
using namespace std;

// 哑元函数
void print_show(int)
{
    cout << "调用了int哑元函数" << endl;
}


int main()
{
    print_show(1);
    return 0;
}

作用1:哑元函数用来区分函数重载
作用2:运算符重载中用到
作用3:保持向前兼容

二.类和对象

类:是一个抽象的概念,用于描述同一类对象的特征。在现在学习阶段,一个单独的类没有任何功能。

对象:根据类的描述创造的实体。

2.1 类的定义

类中主要包含两部分:

属性 property成员变量 member value或 数据成员)
在类中存在的变量,用于存储数据,通常是一个名词,例如身高、价格、颜色......

● 行为(成员函数 member function
可以执行的功能,是类中的函数,通常是一个动词或动词词组,例如:吃饭、运行、关闭......

成员 = 成员变量 + 成员函数

cpp 复制代码
#include <iostream>
using namespace std;
/**
 * @brief The MobilePhone class
 * 手机类
 */
class MobilePhone // 帕斯卡(大驼峰)命名法:所有单词首字母大写
{
public: // 表示访问不受限
    string brand; // 品牌
    string model; // 型号
    int weight; // 重量

    void play_music()
    {
        cout << "Remedy" << endl;
    }
    void run_game()
    {
        cout << "炉石传说" << endl;
    }
    void communicate()
    {
        cout << "喂?" << endl;
    }
};

2.2 创建对象

C++支持两种对象:

● 栈内存对象

在生命周期(生命周期为所在的{})结束后,自动被回收,调用成员使用.

● 堆内存对象

必须使用new关键字创建对象,使用指针保存对象首地址,使用delete销毁对象,如果创建后没有手动销毁,对象会持续存在(内存泄漏),调用成员使用->(在Qt Creator中按.键自动转为->)

cpp 复制代码
#include <iostream>

using namespace std;

class MobilePhone
{
public:
    string brand;
    string model;
    int weight;

    void play_music()
    {
        cout << "Remedy" << endl;
    }

    void run_game()
    {
        cout << "炉石传说" << endl;
    }

    void communicate()
    {
        cout << "喂?" << endl;
    }

};

int main()
{
    // 栈内存对象
    MobilePhone mp1;
    // 调用成员变量,先赋值,再读取输出
    mp1.brand = "苹果";
    mp1.model = "16 Pro Max";
    mp1.weight = 200;
    cout << mp1.brand << endl;
    cout << mp1.model << endl;
    cout << mp1.weight << endl;
    // 调用成员函数
    mp1.communicate();
    mp1.play_music();
    mp1.run_game();

    // 堆内存对象
    MobilePhone* mp2 = new MobilePhone;
    mp2->brand = "华为";
    mp2->model = "非凡大师xt1";
    mp2->weight = 301;
    cout << mp2->brand << endl;
    cout << mp2->model << endl;
    cout << mp2->weight << endl;
    mp2->communicate();
    mp2->play_music();
    mp2->run_game();
    delete mp2; // 销毁mp2
    // 不要销毁后还使用
//    cout << mp2->brand << endl;
//    mp2->communicate();

    return 0;
} // mp1销毁

三. 封装 (重点)

上面的代码与结构体非常相似,因为结构体就是一种完全开放的类。类通常需要进行封装,封装指的是先将类的一些属性和细节隐藏,再重新提供外部调用接口。

cpp 复制代码
#include <iostream>

using namespace std;

class MobilePhone
{
private: // 私有:被修饰的成员只能在类内访问
    string brand; // 读写
    string model = "16"; // 只读
    int weight; // 只写

public:
    string get_brand() // getter:读函数
    {
        return brand;
    }

    void set_brand(string b) // setter:写函数
    {
        brand = b;
    }

    string get_model()
    {
        return model;
    }

    void set_weight(int w)
    {
        weight = w;
    }
};

int main()
{
    MobilePhone mp1;
    // 调用setter设置属性值
    mp1.set_brand("华为");
    mp1.set_weight(300);
    // 调用getter获取属性值
    cout << mp1.get_brand() << endl;
    cout << mp1.get_model() << endl;

    // TODO 可以试试堆内存对象

    return 0;
}

封装的意义在于让程序员站在外部视角看待整个对象整体,忽略内部细节,同时可以提升代码的安全性和可维护性。

四. 构造函数 construct or 重点)

4. 1 基础 使用

类中有一种特殊的成员函数,在创建对象时必须调用,这个函数就是构造函数,构造函数的特殊性体现在:
● 不写返回值
● 函数名称必须是类名
● 如果程序员不手动编写构造函数,编译器会自动添加一个无参且函数体为空的构造函数。
构造函数经常用于在创建对象时进行对象属性初始化,构造函数也支持参数默认值和重载。

cpp 复制代码
#include <iostream>

using namespace std;

class MobilePhone
{
private:
    string brand;
    string model;
    int weight;

public:
    // 编译器会在程序员不写的情况下添加下面的构造函数
//    MobilePhone(){}

    MobilePhone()
    {
        // 给属性赋予初始值
        brand  = "山寨";
        model = "???";
        weight  = 188;
        cout << "构造函数" << endl;
    }

    MobilePhone(string b,string m,int w)
    {
        brand = b;
        model = m;
        weight = w;
        cout << "构造函数2" << endl;
    }

    /**
     * @brief show 输出所有属性值
     */
    void show()
    {
        cout << brand << endl;
        cout << model << endl;
        cout << weight << endl;
    }
};

int main()
{
    MobilePhone mp1;
    mp1.show();

    MobilePhone* mp2 = new MobilePhone;
    mp2->show();
    delete mp2;

    MobilePhone mp3("魅族","Lucky08",199);
    mp3.show();

    MobilePhone* mp4 = new MobilePhone("小米","su7",2100000);
    mp4->show();
    delete mp4;

    cout << "主函数结束" << endl;
    return 0;
}

4. 2 构造 初始化列表

在当前阶段,下面的两种写法是等效。

4. 3 构造 函数 调用 方式 掌握)

构造函数可以显式调用,也可以隐式调用。

显式调用:在创建对象时使用明确的构造函数调用语法,不能被explicit关键字影响。

隐式调用:在创建对象时不使用明确的构造函数调用语法,受到explicit关键字影响

cpp 复制代码
#include <iostream>

using namespace std;

class Student
{
private:
    string name;

public:
    Student(string n):name(n)
    {
        cout << "构造函数,name=" << name << endl;
    }

    string get_name()
    {
        return name;
    }
};


int main()
{
    Student s1("张三");
    cout << s1.get_name() << endl;

    Student* s2 = new Student("李四");
    cout << s2->get_name() << endl;
    delete s2;

    string name = "王五";
    Student s3 = name; // 编译器帮忙调用了构造函数
    cout << s3.get_name() << endl;

    Student s4(name); // 编译器帮忙调用了构造函数
    cout << s4.get_name() << endl;

    return 0;
}

使用explicit关键字可以屏蔽隐式调用的构造函数。

cpp 复制代码
#include <iostream>

using namespace std;

class Student
{
private:
    string name;

public:
    // 明确的
    explicit Student(string n):name(n)
    {
        cout << "构造函数,name=" << name << endl;
    }

    string get_name()
    {
        return name;
    }
};


int main()
{
    Student s1("张三"); // 显式
    cout << s1.get_name() << endl;

    Student* s2 = new Student("李四"); // 显式
    cout << s2->get_name() << endl;
    delete s2;

    string name = "王五";
//    Student s3 = name; // 隐式,被explicit屏蔽
//    cout << s3.get_name() << endl;

    Student s4(name); // 显式
    cout << s4.get_name() << endl;

    return 0;
}

4.4 拷贝构造函数

4.4.1 概念

如果程序员不手动编写拷贝构造函数,编译器会为每个类增加一个拷贝构造函数。

通过拷贝构造函数创建的新对象,只是属性值与源对象相同,但是会在新的地址空间进行开辟。

cpp 复制代码
#include <iostream>

using namespace std;

class Student
{
private:
    string name;

public:
    Student(string n):name(n)
    {
        cout << "构造函数,name=" << name << endl;
    }

    // 编译器自动添加的拷贝构造函数
//    Student(const Student& s)
//    {
//        name = s.name;
//    }


    Student(const Student& s)
    {
        name = s.name;
        cout << "拷贝构造函数" << endl;
    }

    string get_name()
    {
        cout << &name << endl;
        return name;
    }
};


int main()
{
    Student s1("张三");
    cout << s1.get_name() << endl;

    Student s2(s1);
    cout << s2.get_name() << endl;

    return 0;
}

4.4.2浅拷贝

cpp 复制代码
#include <iostream>
#include <string.h> // 头文件

using namespace std;

class Student
{
private:
    char* name;

public:
    Student(char* n):name(n)
    {
        cout << "构造函数" <<  endl;
    }

    char* get_name()
    {
        return name;
    }
};


int main()
{
    char name[20] = "张三";

    Student s1(name);
    Student s2(s1);

    strcpy(name,"李四");

    cout << s1.get_name() << endl; // 李四
    cout << s2.get_name() << endl; // 李四

    return 0;
}

在上面的代码中,在31行修改了26行的字符数组内容,s1和s2隐藏的成员变量的值就变了,且s1和s2的name保存的地址就是26行的name,这都不符合面向对象的特性。

4.4.3深拷贝

cpp 复制代码
#include <iostream>
#include <string.h> // 头文件

using namespace std;

class Student
{
private:
    char* name;

public:
    Student(char* n):name(new char[20])
    {
        // 同步两块内存的内容
        strcpy(name,n);
        cout << "构造函数" <<  endl;
    }

    Student(const Student& s)
    {
        // 同步两块内存的内容
        name = new char[20];
        strcpy(name,s.name);
        cout << "拷贝构造函数" << endl;
    }


    char* get_name()
    {
        return name;
    }
};


int main()
{
    char name[20] = "张三";

    Student s1(name);
    Student s2(s1);

    strcpy(name,"李四");

    cout << s1.get_name() << endl; // 张三
    cout << s2.get_name() << endl; // 张三

    return 0;
}

在上面的深拷贝代码中,new出来的空间并没有delete回收,因此会造成内存泄漏问题。

五. 析构函数 destructor(重点)

析构函数是与构造函数对立的函数,也是一种特殊的成员函数。

|--------------|---------------|
| 构造函数 | 析构函数 |
| 函数名称为类名 | 函数名称为**~类名** |
| 功能为创建对象并初始化 | 功能为在对象销毁时回收资源 |
| 在创建对象时调用 | 在对象销毁时自动被调用 |
| 有参数,支持重载和默认值 | 无参数,不支持重载和默认值 |

程序员不手写析构函数,编译器也会添加一个下面格式的析构函数:

cpp 复制代码
#include <iostream>

using namespace std;

class Dog
{
public:
    ~Dog()
    {
        cout << "析构函数" << endl;
    }
};


int main()
{
    Dog d1;
    Dog* d2 = new Dog;
    delete d2; // d2输出:析构函数

    cout << "主函数结束" << endl;
    return 0;
} // d1输出:析构函数

回到深拷贝代码中,可以在析构函数里释放堆内存资源,在Student中添加下面的代码:

相关推荐
轻口味24 分钟前
命名空间与模块化概述
开发语言·前端·javascript
晓纪同学1 小时前
QT-简单视觉框架代码
开发语言·qt
威桑1 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服1 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
明月看潮生1 小时前
青少年编程与数学 02-004 Go语言Web编程 16课题、并发编程
开发语言·青少年编程·并发编程·编程与数学·goweb
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 17课题、静态文件
开发语言·青少年编程·编程与数学·goweb
Java Fans2 小时前
C# 中串口读取问题及解决方案
开发语言·c#
盛派网络小助手2 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
Chinese Red Guest2 小时前
python
开发语言·python·pygame
一棵星2 小时前
Java模拟Mqtt客户端连接Mqtt Broker
java·开发语言