首页 > 编程语言 >C++ day02(函数、类和对象、封装、构造函数、析构函数)

C++ day02(函数、类和对象、封装、构造函数、析构函数)

时间:2024-09-28 20:19:07浏览次数:7  
标签:构造函数 cout day02 namespace include string 函数

目录

【1】函数

1》内联函数 inline

 2》函数重载 overload 

 3》函数的参数默认(缺省)值

 4》哑元函数

【2】类和对象

1》类的定义

 2》创建对象

 【3】封装

 【4】构造函数 constructor

1》基础使用

2》构造初始化列表

 3》构造函数的调用方式

 4》拷贝构造函数

1> 概念

2> 浅拷贝

3> 深拷贝 

【5】 析构函数 destructor


【1】函数

1》内联函数 inline

在C++中使用内联函数主要是为了取代语言中的宏定义的函数,内联函数在编译时,可以直接展开函数体到主函数中,以此提高程序执行的效率,消除了普通的额外开销。

建议将代码长度较小(1-5行且不能包含复杂控制语句)且频繁使用的函数定义为内联函数,内联函数只是给编译器的建议,编译器并一定采纳。


只需要在函数定义处使用inline 关键字修饰函数,就可以把函数设置成内联函数。

#include <iostream>

using namespace std;

/**
 * @brief test 内联函数
 */
inline void test(int a)
{
    cout << "fdfd" << endl;
    cout << ++a << endl;
}

void func(); // 函数声明

inline void func() // 函数定义
{
    cout << "翻江倒海发的" << endl;
    cout << "法国活动经费" << endl;
}

int main()
{
    int a = 1;
    test(a);
    func();

    return 0;
}

 2》函数重载 overload 

C++ 中允许同一个函数名称定义多个函数,这就是函数重载。

函数签名是编译器区分不同函数的方式,包括一下几个组成部分:

1.函数名称

2.参数数量

3.参数类型

两个函数的签名不可以相同,即一个函数只有一个独一无二的签名。

当函数中函数名称相同,剩余条件不同时(参数数量或参数类型不同),就构成了函数重载。

#include <iostream>

using namespace std;

void test()
{
    cout << "没有参数" << endl;
}

void test(int a)
{
    cout << "一个int参数" << a << endl;
}

void test(int a,int b)
{
    cout << "两个int参数" << a+b << endl;
}

void test(string a,string b)
{
    cout << "两个string参数" << a+b << endl;
}

//int test(string a,string b) 错误
//{
//    cout << "两个string参数" << a+b << endl;
//    return 1;
//}

int main()
{
    test(1,3);
    test(54);
    test();
    test("aa","bb");

    return 0;
}

需要注意的是:构造函数可以重载,析构函数不可以重载

 3》函数的参数默认(缺省)值

C++允许给函数设定默认值,调用函数时如果传入参数,传入的参数会覆盖默认值;调用时如果不传递参数,参数使用预设的默认值。

函数的参数默认值既可以在声明处(建议)设定,又可以在定义处设定,但是只能出现一次。

向右(后)原则:如果函数的参数有多个,只要某个参数设定了默认值,其右边(后面)的所有参数都要设定默认值。

#include <iostream>

using namespace std;

void func(int a = 1)
{
    cout << a << endl;
}

void test(int a = 2);

void test(int a)
{
    cout << a << endl;
}

void method(int a,int b = 1,int c = 2)
{
    cout << a << b << c << endl;
}

int main()
{
    func(); // 1
    func(2); // 2
    test(); // 2
    test(1); // 1
    method(6); // 612
    method(6,6);  // 662
    method(6,6,6); // 666

    return 0;
}

尽量不要同时使用函数重载和参数默认值,因为非常容易出现二义性问题。

#include <iostream>

using namespace std;

void method(int a,int b = 1,int c = 2)
{
    cout << "A"  << a << b << c << endl;
}

void method(int a)
{
    cout  << "B" << a << endl;
}

int main()
{
    method(8,8,8); // A888
    method(5,5); // A552
//    method(9); 错误

    return 0;
}

 4》哑元函数

一个函数的参数只有类型没有名字,这个参数就是哑元。包含哑元的函数就是哑元函数。

#include <iostream>

using namespace std;

void test(int)
{
    cout << "AAA" << endl;
}

int main()
{
//    test(); 错误
    test(34897);

    return 0;
}

哑元函数的实际功能包括但不限于:

1.在C++的运算符重载中区分重载函数

2.保持函数的向前兼容性

【2】类和对象

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

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

1》类的定义

类中主要包含两个部分:

1.属性 property (成员变量  member value 或数据成员)

在类中存在的变量,用于存储数据,通常是一个名词,例如身高、价格、体重......

2.行为 (成员函数 member function)

可以执行的功能,是类中的函数,通常是一个动词或动词词组,例如吃饭、玩游戏、关闭、运行.....

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

 例:定义一个手机类

#include <iostream>

using namespace std;

/**
 * @brief The MobilePhone class
 * 手机类
 */
class MobilePhone // 帕斯卡(大驼峰)命名法:所有单词首字母大写
{
public: // 表示访问不受限
    string brand; // 品牌
    string model; // 型号
    int weight; // 重量

    void play_music()
    {
        cout << "How to Love" << endl;
    }

    void run_game()
    {
        cout << "金铲铲之战" << endl;
    }

    void communicate()
    {
        cout << "摩西摩西" << endl;
    }

};

 2》创建对象

C++支持两种对象:

栈内存对象

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

堆内存对象

必须使用 new 关键字创建对象,使用指针保存对象首地址,使用delete 销毁对象,如果创建之后没有手动销毁,对象会持续存在,造成内存泄漏

#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销毁

 【3】封装

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

#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;
}

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

 【4】构造函数 constructor

1》基础使用

类中有一种特殊的成员函数,在创建对象的必须调用,这个函数就是构造函数,构造函数的特殊性体现在:

1.不写返回值

2.函数名称必须是类名

3.如果程序员不手动编写构造函数,编译器会自动添加一个无参且函数体为空的构造函数。

构造函数经常用于子啊创建对象时进行对象属性初始化,构造函数也支持参数默认值和重载。

#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;
}

2》构造初始化列表

 

 3》构造函数的调用方式

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

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

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

#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;
}

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

#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》拷贝构造函数

1> 概念

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

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

#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;
}

2> 浅拷贝

 当类中的成员变量出现指针时,默认的拷贝构造函数会出现浅拷贝的现象。

#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,这都不符合面向对象的特性。

3> 深拷贝 

深拷贝的思路是在浅拷贝每次对指针进行赋值时,都单独开辟一块内存给要赋值的变量。

#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回收,因此会造成内存泄漏问题。

【5】 析构函数 destructor

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

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

如果程序员没有手动写一个析构函数,编译器也自动添加一个的析构函数:~类名(){}

#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中添加下面的代码:


今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话点个关注支持一下吧! 

标签:构造函数,cout,day02,namespace,include,string,函数
From: https://blog.csdn.net/dghbs/article/details/142615310

相关文章

  • YOLOv10改进策略【损失函数篇】| Shape-IoU:考虑边界框形状和尺度的更精确度量
    一、本文介绍本文记录的是改进YOLOv10的损失函数,将其替换成Shape-IoU。现有边界框回归方法通常考虑真实GT(GroundTruth)框与预测框之间的几何关系,通过边界框的相对位置和形状计算损失,但忽略了边界框本身的形状和尺度等固有属性对边界框回归的影响。为了弥补现有研究的不足,Sh......
  • 字符函数和字符串函数
    字符函数和字符串函数字符分类函数大家知道字符是分为很多种类型的就比如说’a’‘1’'A’等等,所以我们需要一种函数来完成字符函数的分类这就是字符分类函数函数需要包含头文件<ctype.h>函数的运行规则是:如果符合下列参数就返回真inscntrl(控制任何字节)isspace(空白......
  • Day4 C++(运算符重载,模板与容器)(友元函数,运算符重载,赋值运算符,string字符串类,模板)
    1.友元friend1.1概念(掌握)定义:类实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,仅能通过类的成员函数才能读写。如果数据成员定义为公共的,则又破坏了封装性。但是某些情况下,需要频繁读写类的成员,特别是在对某些成员函数多次调用时,由于参数传递、类型检查和安全......
  • 要求实现一个函数 DoubleToStr(double a,int b,char * str),将参数 a 转化为字符串 str
    sprintf函数:sprintf(str,"%.*f",b,a);:sprintf是一个格式化输出函数,类似于printf,但它将输出写入到字符串中而不是标准输出。"%.*f":#include<stdio.h>//将双精度浮点数a转换为字符串str,小数点后保留b位voidDoubleToStr(doublea,intb,char*str){  //......
  • Ini文件读写配置工具类 - C#小函数类推荐
          此文记录的是INI文件的读写工具类。/***Ini文件读写配置工具类AustinLiu刘恒辉ProjectManagerandSoftwareDesignerE-Mail:[email protected]:http://lzhdim.cnblogs.comDate:2024-01-1515:18:00使用说明:......
  • ES6箭头函数的使用
    使用箭头函数的目的:引入箭头函数目的是更加更加简短的函数写法并且不绑定this,箭头函数的语法比函数表达式更加简洁使用场景:箭头函数更加适用于哪些本来需要匿名函数的地方 定义语法:constfn=()=>{}使用方式一:括号里面加形参//箭头函数constfn=(x)=>......
  • 苍穹外卖day02
    文章目录前言一、新增员工需求分析和设计产品原型接口设计数据库设计(employee表)代码开发功能测试代码完善二、员工分页查询需求分析代码开发代码完善三、启用禁用员工账号需求分析代码开发总结前言苍穹外卖day02,主要开发员工模块代码,包含新增员工、员工分页查......
  • 【C语言标准库函数】标准输入输出函数详解2:字符串输入输出
    目录一、字符串输入函数1.1.gets函数(已废弃)1.1.1.函数简介1.1.2.注意和废弃原因1.2.fgets函数1.2.1.函数简介1.2.2.使用场景1.2.3.注意事项1.2.4.示例二、字符串输出函数2.1.puts函数2.1.1.函数简介2.1.2. 使用场景2.1.3.注意事项2.1.4.示例2.2.......
  • Javascript 一题搞懂 var 变量提升 & 函数声明提升!
    前置知识:在JavaScript中,“变量提升”(Hoisting)是指在代码执行之前,变量和函数声明会被提升到其所在作用域的顶部。对于使用var关键字声明的变量,会发生变量提升现象。一、声明提升1.变量声明提升:无论var变量在代码中的何处声明,它都会被提升到其所在的函数作用域......
  • Pandas常用计算函数
    Pandas常用计算函数学习目标知道排序函数nlargest、nsmallest和sort_values的用法知道Pandas中求和、计数、相关性值、最小、最大、平均数、标准偏差、分位数的函数使用1排序函数导包并加载数据集importpandasaspd#加载csv数据,返回df对象df=pd.read_csv('......