C++基础-类与对象(1)

C++类与对象(1)

类的设计:可以把属性和行为放在不同的权限下

struct和class区别在于某人的访问权限不同

  • struct:默认共有
  • class:默认私有

对象的初始化和清理

如果我们不写,系统会自己给我没写

  • 构造函数的语法 类名(){}

没有返回值,也不写void

函数名和类型相同

可以有参,也可以无参

在调用对象会自动调用函数,无需手动调用,只调用一次

  • 析构函数语法 ~类名(){}

同上,不过是无参(不可以重载),销毁时自动调用函数,只调用一次

构造函数的分类与调用

分类

  • 有参和无参(默认)
  • 普通和拷贝

拷贝函数

函数名(const 函数名 &p(对象)){
    age = p.age;
}
#include           //构造函数和析构函数
using namespace std;
class person{
public:
    int age;
public:
    //构造函数
    //普通
    person(){
        cout << "Person无参构造函数的调用" << endl;
    }
    person(int a){
        age = a;
        cout << "Person有参构造函数的调用" << endl;
    }
    //拷贝
    person(const person &p){
        age = p.age;
        cout << "Person拷贝构造函数的调用" << endl;
    }
    //析构函数
    ~person(){
        cout << "Person析构函数的调用" << endl;
    }
};

void test01(){
    //调用
    //1.括号法
    //person p1;
    //person p2(10);
    //person p3(p2);
    //注:调用默认构造函数时,不要加()(系统会以为是一个函数的声明)

    //cout << "p2的年龄:" << p2.age << endl;
    //cout << "p3的年龄:" << p3.age << endl;

    //2.显示法
    person p4;
    person p5 = person(10);//右值:匿名对象,当前行结束,系统收回匿名对象
    person p6 = person(p5);
    //注:不要利用拷贝函数初始化匿名对象

    //3.隐式转换法
    person p7 = 10;//相当于person p5 = person(10);
    person p8 = p7;
}

int main(){
    test01();
 return 0;
}*/

拷贝函构造函数的调用时机

c++中调用拷贝函数一般三种请况

  • 使用一个已经创建完毕的对象来初始化一个对象
  • 值传递的方法给函数参数传值
  • 以值方式返回局部对象
#include          //拷贝时机
using namespace std;

class Person{
private:
    int m_Age;
public:
    Person(){
        cout << "person的默认无参构造函数调用" << endl;
    }

    Person(int age){
        cout << "Person有参构造函数调用" << endl;
        m_Age = age;
    }

    Person(const Person &p){
        cout << "Person的拷贝调用" << endl;
        m_Age =p.m_Age;
    }

    ~Person(){
        cout << "person的析构函数调用" << endl;
    }
};

void test01(){
    Person p1 = Person(20);
    Person p2(p1);
}

void dowork(Person p){  }
void test02(){
    Person p;
    dowork(p);
}

Person dowork2(){
    Person p1;
    cout << (int*)&p1 << endl;  //输出地址
    return p1;
}
void test03(){
    Person p = dowork2();
    cout << (int*)&p << endl;
}

int main(){
    test01();
    cout << endl;
    test02();
    cout << endl;
    test03();
 return 0;
}

C++基础-类与对象(1)

构造函数的调用规则

默认情况下,C++会至少给一个类添加3个函数

  • 默认构造函数无参,函数体为空
  • 默认析构函数无参,函数体为空
  • 默认拷贝构造函数,对属性进行拷贝(所有的属性都进行赋值操作)

规则如下:

  • 如果用户定义了有参构造函数,C++不在提供默认无参构造,但会提供默认的拷贝
  • 如果用户定义了拷贝函数,C++不h会提供其他构造函数

注意:若只定义了拷贝(只有参同理),则

Person p1;
//和
Person p1(20);
//均是错误的(因为系统不会提供)

深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作(如果对其进行释放,则堆区的内存会重复释放,出现错误)

深拷贝:在堆区重新申请空间,进行拷贝操作

m_height = new int(*P.m_height); //new返回的值是地址

private:
int * m_height;  //地址(指针)类型

初始化列表

作用:用来初始化属性

语法:

构造函数():属性1(值1),属性2(值2)...{}

来个浅例吧

#include
using namespace std;

class Person{
public:
    //传统初始化
//  Person(int a,int b,int c){
//      m_A = a;
//      m_B = b;
//      m_C = c;
//  }

    //初始化列表
    Person(int a,int b,int c):m_A(a),m_B(b),m_C(c){} //参数a,b,c可以自由的改变所赋的值

    int m_A;
    int m_B;
    int m_C;
};

void test01(){
    Person p(30,20,10);
    //Person p;
    cout << "m_A:" << p.m_A << endl;
    cout << "m_B:" << p.m_B << endl;
    cout << "m_C:" << p.m_C << endl;
}

int main(int argc, char** argv) {
    test01();
 return 0;
}

结果是30 20 10

类对象作为类的成员

C++类中的成员可以是另一个类的对象,我们成该成员为对象成员

例如:

class A{};
class B{
    A a;
}
//敲黑板(好老的梗...)
//先构造A的对象(即先构造其他类的对象),再构造B的对象
//析构的顺序是相反的,先析构本类,再析构其他类

代码的简单例子

#include
using namespace std;
#include  //要用字符串呢

class Phone{ //类一
public:
    //品牌名字
    string m_Pname;

    Phone(string name){
        m_Pname = name;
    }
};

class Person{ //类二
public:
    //姓名
    string m_Name;
    //手机
    Phone m_Phone;  //类一作为类二的成员

    Person(string Name,string Pname):m_Name(Name),m_Phone(Pname){}
                                              //相当于Phone m_Phone = Pname = Phone(Pname)(隐式转化)
};

void test01(){
    Person p("张三","华为");
    cout << p.m_Phone.m_Pname;
}

int main(int argc, char** argv) {
    test01();
 return 0;
}

结果:华为

静态成员函数

静态成员变量就是加上const

静态成员分成:

  1. 静态成员变量
  2. 所有对象共享一份数据
  3. 再编译阶段分配内存
  4. 类内声明,类外初始化
  5. 静态成员函数
  6. 所有对象共享一个函数
  7. 静态成员函数 只能访问静态成员变量(函数体内无法区分普通变量是那个对象的成员)
  8. 也是 有访问权限的,private下在类外就访问不到
#include
using namespace std;
class Person{
public:
    //静态成员函数
    void static func(){
        m_a = 100;//静态成员函数访问静态成员变量
        cout << "func的调用" << m_a << endl;
    }
    static int m_a;//类内声明类外初始化
};
int Person::m_a = 0;

//两种访问方式
void test01(){

    //通过对象访问
    Person p;
    p.func();

    //通过类名访问
    Person::func();
}

int main(int argc, char** argv) {
    test01();
    return 0;
}

结果

C++基础-类与对象(1)

对象模型和this指针

成员变量和成员函数分开存储

  • 只有非静态成员变量才属于类的对象上面
  • 空对象占用 一个字节

C++编译器会给每个空对象分配一个字节的空间(独一无二的内存地址),防止区分空对象占内存的位置

#include
using namespace std;
class Person1{};
class Person2{
    int m_a;//非静态成员变量,属于类的对象上
    static int m_b;//静态成员变量,不属于类的对象上
    void test01(){}//非静态成员函数,属于类的对象上
    static void test02(){}//静态成员函数,不属于类的对象上
};
int Person2::m_b = 0;

void test01(){//空对象所占用的内存
    Person1 p1;
    cout << "p1 sizeof of p is " << sizeof(p1) << endl;
}

void test02(){//非空对象占用的内存
    Person2 p2;
    cout << "p2 sizeof of p is " << sizeof(p2) << endl;
}

int main(int argc, char** argv) {
    test01();
    test02();
    return 0;
}

C++基础-类与对象(1)

this指针

引子:在上面我们知道,非静态的成员函数只会生成一份函数实例,也是是说多个同类的对象会公用一块代码(一个函数),那么: 这一块代码是如何区分是那个对象调用自己呢?

通过this指针来解决上面的问题,this指针指向被调用的成员函数所属的对象(eg:p1调用就指向p1…)

  • this指针是隐含在每一个非静态成员函数内的一种指针,不用定义,直接使用

用途

  1. 当形参和成员变量重名时,可用this来区分
  2. 在类的非静态成员函数返回对象本身(return *this;)
#include
using namespace std;
class Person1{//名称冲突
public:
    Person1(int age,int age1){
        this->age = age;
        age1 = age1;
    }

    int age;
    int age1;

    Person1 & Add(Person1 &p){
        this->age += p.age;
        //this是一个指向p3的指针,*this就是对象p3的本体
        return *this;
    }
};

void test01(){
    Person1 p1(18,18);
    cout << "p1的年龄是" << p1.age << endl;
    cout << "p1的年龄是" << p1.age1 << endl;
}
void test02(){//把p2的年龄加到p3上
    Person1 p2(10,10);
    Person1 p3(10,10);
    p3.Add(p2).Add(p2);//链式编程思想
    cout << "p3的年龄是" << p3.age << endl;
}

int main(int argc, char** argv) {
    test01();
    test02();
    return 0;
}

结果

C++基础-类与对象(1)

空指针访问成员函数

C++中允许空指针调用成员函数的,但是也要注意有没有用到this地址

如果用到this指针,则需要加以判断确保代码的健壮性

if(this == NULL) return;

看个小例子吧

#include
using namespace std;
class Person{
public:
    void show(){
        cout << "show的调用" << endl;
    }

    int m_age;
    void get(){
        if(this == NULL) return;
        cout << "age=" << m_age << endl;
                      //默认this->m_age
    }
};

void test01(){
    Person * p = NULL;
    //空指针可以访问成员
    p->show();
    p->get();
}

int main(int argc, char** argv) {
    test01();
    return 0;
}

C++基础-类与对象(1)

const修饰成员函数

常函数

  • 不可以修改成员属性
  • 成员属性声明时+mutable关键字,在常函数中就可以修改了
class Person{
public:
    //this本质 指针常量 Person * const this 指向不可以改变
    //在成员函数后面+const const Person * const this,让指针指向的值不可以改变
    void show() const{
        m_a =100;//所以会报错哦
        //其实是this->m_a = 100;
    }
    int m_a;

};

C++基础-类与对象(1)

常对象

  • 常对象 只能调用常函数
const Person p;
p.show();

友元

在程序里,有些私有的属性想让类外的特殊的一些函数或者类进行调用,就需要友元技术

作用(目的):让一个函数或是类访问另一个类中的私有成员

友元关键字: friend(友元:不是类的成员,不受访问限制)

友元的三种实现

  • 全局函数友元
  • 类做友元
  • 成员函数做友元

全局函数做友元

#include
using namespace std;
#include
class Building{
    //goodFriend是Building类的好朋友,可以访问啦
    friend void goodFriend(Building &building);

public:
    Building(){
        SittingRoom = "客厅";
        BedRoom = "卧室";
    }
public:
    string SittingRoom;//客厅
private:
    string BedRoom;//卧室
};

//全局函数
void goodFriend(Building &building){
    cout << "友元全局函数 正在访问:" << building.SittingRoom << endl;
    cout << "友元全局函数 正在访问:" << building.BedRoom << endl;
}

void test01(){
    Building building;
    goodFriend(building);
}

int main(int argc, char** argv) {
    test01();
    return 0;
}

结果

C++基础-类与对象(1)

类做友元

大致流程:

  • 先创建GoodFriend类的对象GF
  • 调用本类下的构造函数:创建一个Building(同时调用Building的构造函数)
  • 访问visit()函数,就可以访问building下的成员啦
#include
using namespace std;
#include

class Building{
    //GoodFriend类是Building类的好朋友
    friend class GoodFriend;

...//和上面一样
};

class GoodFriend{
public:
    GoodFriend(){
        //创建对象
        building = new Building;
    }

    void visit(){//参观函数 访问Building中的属性
        cout << "友元正在访问:" << building->SittingRoom << endl;
        cout << "友元正在访问:" << building->BedRoom << endl;
    }

    Building * building;
};

void test01(){
    GoodFriend GF;
    GF.visit();
}

int main(int argc, char** argv) {
    test01();
    return 0;
}

结果

C++基础-类与对象(1)

成员函数做友元

流程与上面的几乎一样

#include
using namespace std;
#include

class Building;//防止在未创建BUilding类是报错
class GoodFriend{
public:
    Building * building;
    GoodFriend();
    void visit();//参观函数 访问Building中的私有成员
};

class Building{
    //visit()做为BUilding类的好朋友
    friend void GoodFriend::visit();

public:
    string SittingRoom;//客厅
private:
    string BedRoom;//卧室

public:
    Building();
};

//类外声明
Building::Building(){
    SittingRoom = "客厅";
    BedRoom = "卧室";
}

GoodFriend::GoodFriend(){
    building = new Building;
}
void GoodFriend::visit(){//参观函数 访问Building中的私有成员
    cout << "友元正在访问:" << building->SittingRoom << endl;
    cout << "友元正在访问:" << building->BedRoom << endl;
}

void test01(){ //测试函数
    GoodFriend GF;
    GF.visit();
}

int main(int argc, char** argv) {
    test01();
    return 0;
}

结果:

C++基础-类与对象(1)

运算符的重载

概念:对已有运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

对于内置的数据类型,系统知道如何进行运算

加号运算符重载(其他同理)

  1. 成员函数重载+号

本质:Person p3 = p1.operator+(p2);

#include
using namespace std;
class Person{
public:
    int m_A;
    int m_B;
/*======================================================*/
    Person operator+(Person &p){
        Person temp;
        temp.m_A = this->m_A + p.m_A;
        temp.m_B = this->m_B + p.m_B;
        return temp;
    }
/*======================================================*/
};

void test01(){
    Person p1;
    p1.m_A = 10;
    p1.m_B = 10;
    Person p2;
    p2.m_A = 10;
    p2.m_B = 10;

    Person p3 = p1 + p2;

    cout << p3.m_A <

结果是两个20(相加成功)

  1. 全局函数重载+号

本质:Person p3 = operator+(p1,p2);

#include
using namespace std;
class Person{
public:
    int m_A;
    int m_B;
};
/*======================================================*/
Person operator+(Person &p1,Person &p2){
    Person temp;
    temp.m_A = p1.m_A + p2.m_A;
    temp.m_B = p1.m_B + p2.m_B;
    return temp;
}
/*======================================================*/
int main(int argc, char** argv) { //函数和上面的一样
    test01();
 return 0;
}

结果也是两个20

注意:- 运算符的重载也可以发生函数重载(名字相同,参数不同)

       - &#x4E0D;&#x53EF;&#x4EE5;&#x6539;&#x53D8;&#x5185;&#x7F6E;&#x8FD0;&#x7B97;&#x7B26;

C++基础-类与对象(1)

左移运算符的重载(<

Original: https://www.cnblogs.com/wht-de-bk/p/16171665.html
Author: T,a,o
Title: C++基础-类与对象(1)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570999/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 将链表按照左右分区重新排列

    将链表按照左右分区重新排列 问题重述: 给定一个单链表 L 的头节点 head ,单链表 L 表示为: L0 &#x2192; L1 &#x2192; &#…

    Java 2023年6月7日
    069
  • Linux 终端运行命令时出现多行带有加号的信息(详见文章内容)

    ++_vte_ prompt_ command +++ HISTTIMEFORMAT= +++ history 1 +++ sed ‘s/^ *[0-9] \+ *//’ ++ l…

    Java 2023年6月8日
    081
  • IO流

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月7日
    088
  • Redis的中并发问题的解决方案小结

    什么是Redis的并发竞争问题 Redis的并发竞争问题,主要是发生在并发写竞争。考虑到redis没有像db中的sql语句,update val = val + 10 where …

    Java 2023年6月9日
    080
  • 发送POST请求时发生 java-org.springframework.http.converter.HttpMessageNotReadableException

    本质上就是因为尝试用@RequestBody 接收一个变量,但是这样spring是不允许的,所以需要专门写一个类或者直接用实体类接收,这样就可以了. https://www.jb5…

    Java 2023年5月29日
    0102
  • (读书笔记)基于CMMI的软件工程及实训指导 第13-16章

    一、软件测试 软件测试是为了发现程序中的错误而执行的过程。测试只能证明软件有错,而不能保证软件程序没错。 1. 软件版本 Alpha版 公司内测版本 Beta版 对外公测版本 发布…

    Java 2023年6月5日
    0106
  • 为什么用Redis做排行榜?

    数据结构方面:Redis的sorted set结构用来排名很方便,谁得分高谁排名往上。 排行榜是时限性业务。具有周期性。超出时间,自动删除。Redis的超时设置很好实现。 排行榜是…

    Java 2023年6月15日
    095
  • java学习之动态代理

    在后面的漏洞研究的学习中,必须要会的几个知识点。反射机制和动态代理机制。至于反射的前面已经讲到过了,这里就不做更多的赘述了。反射是通过class文件去获取对象对象的方法. &amp…

    Java 2023年6月13日
    085
  • synchronized锁及其锁升级

    点赞再看,养成习惯,微信搜索「 小大白日志」关注这个搬砖人。 文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。 多线程加锁有两种方式 利用Sychronized关…

    Java 2023年6月8日
    088
  • 尚医通项目总结(一)———-微服务、Spring Boot和Spring Cloud的区别

    跟着尚硅谷做了尚医通项目来学习,原本以为知识点并不是很多,面试后发现如果深挖,有很多需要学习理解的点,此系列博客记录项目中涉及的知识点,也希望能帮助到做了同一个项目的同学。博客中的…

    Java 2023年6月5日
    090
  • Java static 关键字

    static可以用来修饰类的成员方法、类的成员变量,也可以编写static代码块来优化程序性能。 注意:1、被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类…

    Java 2023年6月5日
    083
  • Error: Invalid or corrupt jarfile SpringBootTemplate.jar

    当在尝试将SpringBoot打包成为Jar文件, 丢到linux服务器去运行的时候, 尝试在windows自带的CMD窗口命令行中运行jar文件的时候, 遇到了这样的问题. 错误…

    Java 2023年5月29日
    076
  • idea控制台

    快捷键:alt + 4 本文来自博客园,作者:紫英626,转载请注明原文链接:https://www.cnblogs.com/recorderM/p/15991828.html O…

    Java 2023年6月5日
    0110
  • 变量 + 数据类型

    //引入头文件 #include void main(){ float a = 10.1; //"初始化": 从"double"到&quot…

    Java 2023年6月16日
    0112
  • Canal-监听数据库表的变化

    1. 简介 Canal是阿里巴巴旗下的一款开源项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&消费功能。 工作原理 Mysql主备复制原理 MySQL ma…

    Java 2023年6月7日
    0120
  • 微服务SpringCloud之服务注册与发现

    在找.net core 微服务框架时发现了Steeltoe开源项目,它可以基于Spring Cloud实现.net core和.net Framework的微服务。正好之前也有学习…

    Java 2023年5月30日
    094
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球