C++基础从0到1入门编程(四)类和对象

news2024/11/18 21:44:06

系统学习C++
方便自己日后复习,错误的地方希望积极指正
往期文章:
C++基础从0到1入门编程(一)
C++基础从0到1入门编程(二)
C++基础从0到1入门编程(三)
参考视频:
1.黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难
2.系统化学习C++


1 从结构体到类

面向对象编程,一切都是对象,对象用类来描述
类:成员变量(属性)和函数(操作方法)

class 类名
{
public:
	成员1的数据类型 成员名1;
	成员2的数据类型 成员名2;
	...
	成员n的数据类型 成员名n;
}

用类定义一个类的变量:创建(或实例化)一个对象
对象的成员变量和成员函数的作用域和生命周期与对象的作用域和生命周期相同

#include <iostream>
using namespace std;
class CGirl
{
public:
    string name;
    int    age;
    void setvalue(string name, int age);
    void show()
    {
        cout << "name = " << name << endl << "age = " << age << endl;
    }
};
void CGirl::setvalue(string name, int age)
{
    this->name = name;
    this->age = age;
}
int main()
{
    CGirl girl;
    girl.setvalue("BigDavid", 23);
    girl.show();
    return 0;
}

2 类的访问权限

类的成员访问权限:public、private、protected
在类的内部,无论成员声名为何种权限,都可以访问
在类的外部(定义类的代码之外的代码),只能访问public成员

#include <iostream>
using namespace std;
class CGirl
{
private:
    string name;
    int    age;
public:
    void setvalue(string name, int age);
};
void CGirl::setvalue(string name, int age)
{
    this->name = name;
    this->age = age;
}
int main()
{
    CGirl girl;
    girl.setvalue("BigDavid", 23); // 可以通过
    return 0;
}

(1)在一个类定义中,private和public可以出现多次
(2)结构体成员缺省为public,类成员缺省为private
(3)private的意义在于隐藏类的数据和实现,把需要向外暴露的成员声名为public

3 简单使用类

(1)类的成员函数可以直接访问该类其他的成员函数(可以递归)

#include <iostream>
using namespace std;
class CGirl
{
private:
    string name;
    int    age;
    int    times = 0;
public:
    void setvalue(string name, int age)
    {
        this->name = name;
        this->age = age;
        show();
    }
    void show()
    {
        if (times++ > 10) return;
        cout << name << ' ' << age << endl;
        show();
    }
};

int main()
{
    CGirl girl;
    girl.setvalue("BigDavid", 23);
    return 0;
}

(2)类的成员函数可以重载,可以用默认参数
(3)类的成员可以是任意数据类型(类中枚举

class CGirl
{
private:
    string name;
    int    age;
    int    times = 0;
    enum sex {girl = 1, boy = 2};
public:
    void setvalue(string name, int age)
    {
        this->name = name;
        this->age = age;
        show();
    }
    void show()
    {
        if (times++ > 10) return;
        cout << name << ' ' << age << endl;
        show();
    }
};

(4)可以为类的成员指定缺省值
(5)类可以创建对象数组
(6)对象可以作为实参传递给函数,一般传引用
(7)用new动态创建对象,用delete释放对象
(8)在类的外部,一般不直接访问(读和写)对象的成员,而是用成员函数。数据隐藏是面向对象编程的思想
(9)对象一般不用memset()清空成员变量,可以写一个专用于清空成员变量的成员函数

class CGirl
{
private:
    string name;
    int    age;
    int    times = 0;
    enum sex {girl = 1, boy = 2};
public:
    void setvalue(string name, int age)
    {
        this->name = name;
        this->age = age;
        show();
    }
    void initdata()
    {
        name.clear();
        age = 0;
    }
    void show()
    {
        if (times++ > 10) return;
        cout << name << ' ' << age << endl;
        show();
    }
};

(10)对类和对象用sizeof运算意义不大,一般不用
(11)用结构体描述纯粹的数据,用类描述对象
(12)在类的声明中定义的函数都将自动成为内联函数;在类的声明之外定义的函数如果使用了inline限定符,也是内联函数
(14)区分类的成员变量和成员函数的形参,把成员变量名加m_前缀或_后缀,如 m_name, name_
(15)类分文件编写

4 构造函数和析构函数

构造函数:在创建对象时,自动的进行初始化工作
语法:类名(){...}

可以有参数,可以重载,可以有默认参数
创建对象只会自动调用一次,不能手工调用

析构函数:在销毁对象前,自动完成清理工作
语法:~类名(){...}

没有参数,不能重载
销毁对象前只会自动调用一次,但可以手工调用

构造函数的细节
(1)如果没有提供构造/析构函数,编译器将提供空实现的构造/析构函数
(2)如果提供了构造/析构,编译器将不提供空的构造/析构函数
(3)创建对象时,如果重载了构造函数,编译器根据实参匹配相应的构造函数。没有参数的构造函数也叫默认构造函数
(4)创建对象的时候不要在对象名后面加空的圆括号,编译器误认为是声明函数。(如果没有构造函数、构造函数没有参数、构造函数的参数都有默认参数)
(5)在构造函数名后面加括号和参数不是调用构造函数,是创建匿名对象
(6) 接受一个参数的构造函数允许使用赋值语法将对象初始化为一个值(可能会导致问题)

CGirl girl = 10;

(7)第一行代码构造函数和析构函数调用一次,下面两行调用两次

CGirl girl = CGirl("西施"20);  // 显式创建对象。
CGirl girl;                   // 创建对象。
girl = CGirl("西施"20);        // 创建匿名对象,然后给现有的对象赋值

(8)用new/delete创建/销毁对象时,也会调用构造/析构函数
(9)不建议在构造/析构函数中写太多的代码,可以调用成员函数。
(10)除了初始化,不建议让构造函数做其他工作
(11)C++11支持使用统一的初始化列表

CGirl girl = {"BigDavid", 8};
CGirl girl {"BigDavid", 8};
CGirl* girl = new CGirl{"BigDavid", 8};

(12)如果类的成员也是类,创建对象的时候,先构造成员类;销毁对象的时候,先析构自身,再析构成员类

5 拷贝构造函数

用一个已经存在的对象创建新的对象,不会调用(普通)构造函数,会调用拷贝构造函数。
如果类中没有定义拷贝构造函数,编译器将提供一个拷贝构造函数,它的功能是把已存在对象的成员变量赋值给新对象的成员变量

类名 新对象名(已存在的对象名)
类名 新对象名 = 已存在的对象名

拷贝构造函数的语法:
类名(const 类名& 对象名){...}
Tip:
(1)访问权限必须是public
(2)函数名必须与类名相同
(3)如果类中定义了拷贝构造函数,编译器将不提供默认的拷贝构造函数
(4)以值传递的方式调用函数时,如果实参为对象,会调用拷贝构造函数
(5)函数以值的方式返回对象时,可能会调用拷贝构造函数(VS会调用,Linux不会,g++编译器做了优化)
(6)拷贝构造函数可以重载,可以有默认参数
类名(......,const 类名& 对象名,......){......}
(7)如果类中重载了拷贝构造函数却没有定义默认的拷贝构造函数,编译器也会提供默认的拷贝构造函数

6 浅拷贝和深拷贝

在这里插入图片描述

(1)如果一个对象修改了内存中的数据,会影响另一个对象
(2)其中一个对象释放了内存,另一个对象的指针就成了野指针

#include <iostream>
using namespace std;

class CGirl
{
public:
    string m_name;
    int    m_age;
    int*   m_ptr;
    CGirl()
    {
        m_name.clear();
        m_age = 0;
        m_ptr = nullptr;
        cout << "gouzaohanshu\n";
    }
    CGirl(const CGirl& gg)
    {
        m_name = gg.m_name;
        m_age = gg.m_age;
        m_ptr = gg.m_ptr;
    }
    ~CGirl()
    {
        delete m_ptr;
        m_ptr = nullptr;
        cout << "123\n";
    }
    void show()
    {
        cout << m_name << ' ' << m_age << ' ' << *m_ptr << endl;
    }
};

int main()
{
    CGirl g1;
    g1.m_name = "BigDavid";
    g1.m_age = 23;
    g1.m_ptr = new int(3);
    g1.show();
    CGirl g2(g1);
    *g2.m_ptr = 8;
    g1.show();
    g2.show();
}

在这里插入图片描述

指针A指向一块内存,重新分配一块相同大小的内存,让指针B指向新内存。再把指针A指向的内存中的数据拷贝到新内存中

CGirl(const CGirl& gg)
{
    m_name = gg.m_name;
    m_age = gg.m_age;
    m_ptr = gg.m_ptr;
}

修改上面代码

CGirl(const CGirl& gg)
{
	m_name = gg.m_name;
    m_age = gg.m_age;
    // 分配内存
    m_ptr = new int;
    // *m_ptr = *gg.m_ptr; // 拷贝数据
    memcpy(m_ptr, gg.m_ptr, sizeof(int)); // 拷贝数据
}

7 初始化列表

构造函数的执行分为两个阶段:初始化阶段和计算阶段
初始化阶段先于计算阶段执行
构造函数除了有形参列表和函数体之外,还可以有初始化列表。
初始化列表的语法:
类名(形参列表):成员1(值),成员2(值),...,成员n(值) {...}
Tip:
(1)如果成员已经在初始化列表中,则不应该在构造函数中再次赋值,会覆盖之前初始化列表的值
(2)初始化列表中的括号可以是具体值,也可以是构造函数的形参名,还可以是表达式
(3)初始化列表与赋值有本质的区别,如果成员是类,使用初始化列表调用的是成员类的拷贝构造函数,而赋值则是先创建成员类的对象(将调用成员类的普通构造函数),然后再赋值
(4)成员是类,初始化列表对性能略有提升
(5)如果成员是常量和引用,必须使用初始列表,因为常量和引用只能在定义的时候初始化
(6)如果成员是没有默认构造函数的类,则必须使用初始化列表
(7)拷贝构造函数也可以有初始化列表
(8)类的成员变量可以不出现在初始化列表中
(9)构造函数的形参先于成员变量初始化

#include <iostream>
using namespace std;

class CBoy
{
public:
    string m_xm;
    CBoy()
    {
        m_xm.clear();
        cout << "CBoy()\n";
    }
    CBoy(string xm)
    {
        m_xm = xm;
        cout << "CBoy(string xm)\n";
    }
    CBoy(const CBoy& bb)
    {
        m_xm = bb.m_xm;
        cout << "CBoy(const CBoy &bb)\n";
    }
};
class CGirl
{
public:
    string m_name;
    const int    m_age;
    CBoy  m_boy;
    CGirl(string name, int age, CBoy &boy):m_name(name), m_age(age) , m_boy(boy)
    {
        //m_boy.m_xm = boy.m_xm;
        cout << "CGirl(name,age,boy)\n";
    }
    void show()
    {
        cout << m_name << ' ' << m_age << ' ' << m_boy.m_xm << endl;
    }
};

int main()
{
    CBoy boy("BigDavid");

    CGirl g1("qwe", 18, boy);

    g1.show();
}

8 对象和类 - const修饰成员函数

在类的成员函数后面加const关键字,表示在成员函数中保证不会修改调用对象的成员变量
(1)mutable可以突破const限制,被mutable修饰的成员变量,将永远处于可变的状态
(2)非const函数可以调用const和非const函数
(3)const成员函数不能调用非const成员函数
(4)非const对象可以调用const修饰的成员函数和非const修饰的成员函数
(5)const对象只能调用const修饰的成员函数,不能调用非cosnt修饰的成员函数
Tip:

const CGirl g1("asd", 18);
g1.show();

常对象只能访问加了const的成员函数
创建g1时,上面代码相当于访问了构造函数,但是构造函数没有加const,也没有报错,如果给构造函数加了const,报错了,原因是构造函数或析构函数不允许使用类型限定符

9 this指针

如果类的成员函数涉及多个对象,在这种情况下需要使用this指针
this指针存放了对象的地址,被作为隐藏参数传递给了成员函数,指向调用成员函数的对象(调用者对象)

每个成员函数(包括构造函数和析构函数)都有一个this指针,可以用它访问调用者对象的成员。可以解决成员变量名和函数形参名相同的问题

int aa;
void func(int aa)
{
	this->aa = aa;
}

*this可以表示对象
如果在成员函数的括号后面使用const,那么将不能通过this指针修改成员变量

#include <iostream>
using namespace std;

class CGirl
{
public:
    string m_name;
    int    m_yz;

    CGirl(const string &name, int yz)
    {
        m_name = name;
        m_yz = yz;
    }
    void show() const
    {
        cout << m_name << " beautiful\n";
    }
    const CGirl& pk(const CGirl& g) const
    {
        if (g.m_yz < m_yz) return g;
        return *this;
    }
};

int main()
{
    CGirl g1("a", 5);
    CGirl g2("b", 6);
    CGirl g3("c", 1);
    const CGirl& g = g1.pk(g2).pk(g3); // c beautiful
    g.show();
}

10 类的静态成员

类的静态成员:静态成员变量静态成员函数
静态成员可以实现多个对象之间的数据共享,比全局变量更安全
static关键字把类的成员变量声名为静态,表示在程序中(不仅是对象)是共享的

静态成员变量不会在创建对象的时候初始化,必须在程序的全局区用代码清晰的初始化(用范围解析运算符 ::)
静态成员使用类名加范围解析运算符 :: 就可以访问,不需要创建对象。
如果把类的成员声明为静态的,就可以把它与类的对象独立开来(静态成员不属于对象)

静态成员变量在程序中只有一份(生命周期与程序运行期相同,存放在静态存储区的),不论是否创建了类的对象,也不论创建了多少个类的对象

静态成员函数,只能访问静态成员,不能访问非静态成员
非静态成员函数可以访问静态成员

静态成员函数没有this指针
const静态成员变量可以在定义类的时候初始化

#include <iostream>
using namespace std;

class CGirl
{
    static int m_age;
public:
    string     m_name;
    CGirl(const string& name, int age)
    {
        m_name = name;
        m_age = age;
    }
    void show()
    {
        cout << "name: " << m_name << endl;
    }
    static void showage()
    {
        cout << m_age << endl;
    }
};

int CGirl::m_age = 22;

int main()
{
    CGirl g1("a", 21), g2("b", 23), g3("c", 24);
    g1.show(); g1.showage();
    g2.show(); g2.showage();
    g3.show(); g3.showage();

    CGirl::showage();
}

11 简单对象模型

C语言本身没有支持数据和函数之间的关联性,数据和处理数据的操作是分开的。

C++用类描述抽象数据类型(ADT),在类中定义了数据和函数,把数据函数关联起来

对象维护了多个指针表,表中存放了成员和地址的对应关系
在这里插入图片描述
数据成员:非静态、静态
函数成员:非静态、静态、virtual

对象内存的大小:
(1)所有非静态数据成员的大小
(2)由内存对齐而填补的内存大小
(3)支持virtual成员而产生的额外负担

静态成员变量属于类,不计算在对象的大小之内
成员函数是分开存储的,不论对象是否存在都占用存储空间,在内存中只有一个副本,也不计算在对象大小之内
用空指针可以调用没有用到this指针的非静态成员函数(如果成员函数中没有用到非静态成员变量,就可以用空指针调用它)
在没有创建对象的情况下,访问非静态成员变量就是访问空指针

#include <iostream>
#include <cstring>
using namespace std;

class CGirl
{
public:
    char       m_name[3];
    int        m_bh;
    static int m_age;

    CGirl()
    {
        memset(m_name, 0, sizeof(m_name)); m_age = 0;
    }
    ~CGirl() {}
    void showname()
    {
        //if (this == nullptr) return;
        cout << "asd" << endl;
    }
    void showage()
    {
        cout << m_age << endl;
    }
};

int CGirl::m_age;
int aaa;
void func() {}

int main()
{
    CGirl g;
    CGirl* g1 = nullptr;
    g1->showname(); // asd
}

对象的地址是第一个非静态成员变量的地址,如果类中没有非静态成员变量,编译器会隐含的增加一个1字节的占位成员

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1241813.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker启动容器失败,然后查看日志,docker logs查看容器出现报错:

docker 启动容器失败&#xff0c;然后docker logs 查看容器出现报错&#xff1a; error from daemon in stream: Error grabbing logs: invalid character l after object key:value pair在网上看到的 解决方案&#xff1a; 找到你日志文件目录&#xff1a; docker inspect …

信用卡不在身上怎么查安全码

信用卡安全码是由3位数字组成的&#xff0c;一般位于信用卡背面签名栏旁边。如果信用卡不在身上&#xff0c;可以通过拨打发卡银行客服热线来查询安全码。但是&#xff0c;安全码是非常私密的信息&#xff0c;客服可能没有权限查询。因此&#xff0c;这个方法不一定有用。另外&…

Ocam——自由录屏工具~

当我们想要做一些混剪、恶搞类型的视频时&#xff0c;往往需要源影视作品中的诸多素材&#xff0c;虽然可以通过裁减mp4文件的方式来获取片段&#xff0c;但在高画质的条件下&#xff0c;mp4文件本身通常会非常大&#xff0c;长此以往&#xff0c;会给剪辑工作带来诸多不便&…

芯片的测试方法

半导体的生产流程包括晶圆制造和封装测试&#xff0c;在这两个环节中分别需要完成晶圆检测(CP, Circuit Probing)和成品测试(FT, Final Test)。无论哪个环节&#xff0c;要测试芯片的各项功能指标均须完成两个步骤&#xff1a;一是将芯片的引脚与测试机的功能模块连接起来&…

CCC联盟——UWB MAC(一)

本文在前面已经介绍了相关UWB的PHY之后&#xff0c;重点介绍数字钥匙&#xff08;Digital Key&#xff09;中关于MAC层的相关实现规范。由于MAC层相应涉及内容比较多&#xff0c;本文首先从介绍UWB MAC的整体框架&#xff0c;后续陆续介绍相关的网络、协议等内容。 1、UWB MAC架…

好用的团队协同办公软件推荐!企业办公必备!

有什么好用的团队协同办公软件可以推荐&#xff1f; 想要的办公软件需要满足“即时通讯”、“多端适配”、“项目管理”、“文件传输”这4大能力。 下面就给大家分享3大类能够满足题主需求的企业级办公软件&#xff0c;免费的付费的都有&#xff0c;也都是侧重的不同领域&…

基于命令行模式设计退款请求处理

前言 这篇文章的业务背景是基于我的另一篇文章: 对接苹果支付退款退单接口-CSDN博客 然后就是说设计模式是很开放的东西,可能我觉得合适,你可能觉得不合适,这里只是做下讨论,没有一定要各位同意的意思.... 相关图文件 这里我先把相关的图文件放上来,可能看着会比较清晰点 代码逻…

[UE4][C++]基于UUserWidget的一种序列图播放方法

最近在做一个大项目&#xff0c;鸽了几个月了....... 一、传统方法Flipbook 这种方法适合序列图较少的情况下、可以一个一个添加进来然后调整顺序。蓝图也比较友好可以直接设置很多属性和功能。这里简单了解一下即可&#xff0c;想要深入了解的同学可以自行搜索。 1.1创建Fli…

手把手云开发小程序-(四)-uniclould增删改查业务开发

一&#xff0c;导入uView 在开发小程序的时候&#xff0c;我习惯使用uView这个ui库。主要是直接用当然比自己写省时间。 它的官网&#xff1a;uView - 多平台快速开发的UI框架 - uni-app UI框架 (gitee.io) 导入&#xff1a; npm install uview-ui2.0.31然后按照官网进行配…

数据库基础入门 — SQL运算符

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

Qt 软件开发框架(主要几个部分)

目录 1、 一个软件基本要素 &#xff08;1&#xff09;UI模块 &#xff08;2&#xff09;网络模块 &#xff08;3&#xff09;业务逻辑模块 &#xff08;4&#xff09;中间层 &#xff08;5&#xff09;独立模块&#xff08;守护进程、更新模块、日志收集模块…&#xff…

CodeWhisperer 体验总结

CodeWhisperer 体验总结 | CodeWhisperer 是一款亚马逊新推出的通用代码生成器 可以实时进行代码数据的提供 还可以定义安全问题 CodeWhisperer 对个人用户是免费使用 企业用户需要订阅使用 亚马逊云科技开发者社区为开发者们提供全球的开发技术资源。这里有技术文档、开发案例…

电商数据API接口接入|获取京东商品详情数据价格主图描述

企业前台研发部包含了企业业务大部分的对外前台系统&#xff0c;其中京东VOP平台(开放平台)适合于自建内网采购商城平台的企业客户。京东为这类客户专门开发API接口&#xff0c;对接到客户内网的网上商城&#xff0c;将产品SKU直接推送到客户内网&#xff0c;客户内部采购人员可…

华中科技大学李松课题组,利用机器学习预测多孔材料水吸附等温线

By 超神经 多孔材料的水吸附等温线是一个非常重要的参数&#xff0c;但这一参数的获得并不容易。这是因为多孔材料种类过多、结构多元&#xff0c;通过实验和计算的方式获得水吸附等温线数据成本过高&#xff0c;耗时过长。 华中科技大学的李松课题组&#xff0c;建立了一个两步…

开发板启动进入系统以后再挂载 NFS 文件系统, 这里的NFS文件系统是根据正点原子教程制作的ubuntu_rootfs

如果是想开发板启动进入系统以后再挂载 NFS 文件系统&#xff0c;开发板启动进入文件系统&#xff0c;开发板和 ubuntu 能互相 ping 通&#xff0c;在开发板文件系统下新建一个目录 you&#xff0c;然后执行如下指令进行挂载&#xff1a; mkdir mi mount -t nfs -o nolock,nfsv…

MySQL中自增id用完怎么办?

MySQL中自增id用完怎么办&#xff1f; MySQL里有很多自增的id&#xff0c;每个自增id都是定义了初始值&#xff0c;然后不停地往上加步长。虽然自然数是没有上限的&#xff0c;但是在计算机里&#xff0c;只要定义了表示这个数的字节长度&#xff0c;那它就有上限。比如&#…

【LeetCode二叉树进阶题目】606. 根据二叉树创建字符串,102. 二叉树的层序遍历,107. 二叉树的层序遍历 II

二叉树进阶题目 606. 根据二叉树创建字符串解题思路及实现 102. 二叉树的层序遍历解题思路及实现 107. 二叉树的层序遍历 II解题思路及实现 606. 根据二叉树创建字符串 描述 给你二叉树的根节点 root &#xff0c;请你采用前序遍历的方式&#xff0c;将二叉树转化为一个由括号…

1553. 吃掉 N 个橘子的最少天数(记忆化+贪心优化)

Problem: 1553. 吃掉 N 个橘子的最少天数 文章目录 题目思路Code 题目 使得 n 变成0的操作有三种方式 &#xff1a; 吃掉一个橘子。如果剩余橘子数 n 能被 2 整除&#xff0c;那么你可以吃掉 n/2 个橘子。如果剩余橘子数 n 能被 3 整除&#xff0c;那么你可以吃掉 2*(n/3) 个…

xorm源码学习

文章目录 XORM源码浅析及实践ORMORM vs. SQLXORM软件架构 ORM 引擎 Engine——DBM*core.DB Golang&#xff1a;database/sql 源码基本结构连接复用&#xff0c;提高性能。增加数据库连接池数量连接管理 database/sql主要内容&#xff1a;sql.DB创建数据库连接sql.Open()DB.conn…

python数据结构与算法-15_堆与堆排序

堆(heap) 前面我们讲了两种使用分治和递归解决排序问题的归并排序和快速排序&#xff0c;中间又穿插了一把树和二叉树&#xff0c; 本章我们开始介绍另一种有用的数据结构堆(heap)&#xff0c; 以及借助堆来实现的堆排序&#xff0c;相比前两种排序算法要稍难实现一些。 最后我…