2-C++的类与对象、封装、构造函数、拷贝、构析函数、作用域限定符的运用和explicit关键字的相关知识点

本文主要是介绍2-C++的类与对象、封装、构造函数、拷贝、构析函数、作用域限定符的运用和explicit关键字的相关知识点,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.类和对象

2.封装

3.构造函数

3.1 固定值的构造函数

3.2 有参的构造函数

3.3 构造函数支持重载

4.深拷贝与浅拷贝()

5.析构函数

6.作用域限定符::

6.1 名字空间

6.2类内声明 内外定义 

7.explicit关键字


1.类和对象

Car类 属性:brand 品牌 seat 座位数量 wheel 轮子数量

方法 run() xx牌的汽车在行驶

show() 打印出上面的属性信息

分别实例化栈和堆内存对象。来调用方法

#include <iostream>
using namespace std;
class Car{
public://brand 品牌  seat 座位数量   wheel 轮子数量
    string brand;int seat;int wheel;void run(){
        cout<<brand<<"牌的汽车在行驶"<<endl;}void show(){
        cout<<"品牌:"<<brand<<" 座位数量:"<<seat<<" 车轮数量:"<<wheel<<endl;}};
int main()
{//栈内存对象
    Car car1; //创建car1对象
    car1.brand="奥迪";
    car1.seat=5;
    car1.wheel=4;
    car1.run();
    car1.show();//堆内存对象
    Car * car2=new Car;
    car2->brand="宝马";
    car2->seat=4;
    car2->wheel=4;
    car2->run();
    car2->show();delete car2;
    car2=NULL;}

写一个长方形的类 Rectangle .

属性 长length 宽 width

方法 面积 area 周长 perimeter

#include <iostream>
using namespace std;
class Rectangle{
public:int width;int length;void area(){
        cout<<"面积是:"<<width*length<<endl;}int perimeter(){return (width+length)*2;}
};
int main()
{
    Rectangle r1;
    r1.width=10;
    r1.length=20;
    r1.area();
    cout<<r1.perimeter()<<endl;
}

2.封装

概念:将类中的一些属性和具体实现细节隐藏。通常是把属性设为私有。如需访问需要通过公共接口。封装可以控制属性的读和写的权限,提高程序的安全性

#include <iostream>
using namespace std;
class Person{
private: //私有权限 类外不能访问string name;     //姓名  可读可写string address;  //地址  只读string password; //密码  只写
public:void get_name(){
        cout<<"姓名是:"<<name<<endl;}void set_name(string n){
        name=n;}void get_address(){
        cout<<"济南银荷大厦"<<endl;}void setPassword(string p){
        password=p;
        cout<<"密码重置成功!"<<endl;}};
int main()
{Person p;
    p.set_name("小明");
    p.get_name();
    p.get_address();
    p.setPassword("123789#");//cout<<p.password<<endl;  //封装属性为私有  类外就访问不到}

3.构造函数

概念:用于创建对象时初始化对象中的属性。

特点:

  1. 实例化对象时,必须调用构造函数进行初始化。
  2. 如果不显示的给出构造函数,会有一个默认的构造函数,参数为空,函数体为空,并没有实际意义
  3. 如果给出任意构造函数,默认无参的构造函数就不存在了

构造函数是个特殊的函数:

  1. 名字和类同名
  2. 不需要写返回值

3.1 固定值的构造函数

可以给出有固定值的构造函数。这样的构造函数不需要传参

#include <iostream>
using namespace std;
class Computer{
private:string brand;string model;int weight;public://默认的构造函数
//    Computer(){//    }Computer(){
        cout<<"有固定值的构造函数调用"<<endl;
        brand="联想";
        model="air14";
        weight=200;}void show(){
        cout<<"brand:"<<brand<<" model:"<<model<<" weight:"<<weight<<endl;}};
int main()
{Computer c1;
    c1.show();
}

3.2 有参的构造函数

用参数给属性赋予初始值,让对象的创建更加灵活

#include <iostream>
using namespace std;
class Computer{
private:
    string brand;
    string model;
    int weight;public:
    Computer(string b,string m,int w){
        brand=b;
        model=m;
        weight=w;    }
    Computer(){
        cout<<"有固定值的构造函数调用"<<endl;
        brand="联想";
        model="air14";
        weight=200;
    }
    void show(){
        cout<<"brand:"<<brand<<" model:"
           <<model<<" weight:"<<weight<<endl;
    }};
int main()
{
    Computer c1;
    c1.show();    Computer c2("联想","air15",240); //有参构造创建的对象c2
    c2.show();    Computer * c3=new Computer("联想","拯救者",270);
    c3->show();}

3.3 构造函数支持重载

3.4 构造初始化列表

#include <iostream>
using namespace std;
class Computer{
private:
    string brand;
    string model;
    int weight;public:
    //第一种方式 普通构造函数
//    Computer(string b,string m,int w){
//        brand=b;
//        model=m;
//        weight=w;//    }
    //第二种方式 构造初始化列表
    Computer(string b,string m,int w):brand(b),model(m),weight(w){}
    void show(){
        cout<<"brand:"<<brand<<" model:"
           <<model<<" weight:"<<weight<<endl;
    }};
int main()
{
    Computer c2("联想","air15",240); //有参构造创建的对象c2
    c2.show();    Computer * c3=new Computer("联想","拯救者",270);
    c3->show();}

3.4 构造函数支持默认值

幼儿园老师Kids_Teacher

属性有姓名,性别(性别默认是女),年龄。并给出属性的读取和写入接口。

给出show方法可以打印出上述属性,用构造函数的方式实例化对象

#include <iostream>
using namespace std;
class Kids_Teacher {
private: //姓名,性别(性别默认是女),年龄
    string name;
    string sex;
    int age;
public:
    Kids_Teacher(string n,int a,string s="女"){
        name=n;
        sex=s;
        age=a;
    }
    //属性的读写接口
    string getName(){
        return name;
    }
    void setName(string n){
        name=n;
    }
    void show(){
        cout<<name<<" "<<sex<<" "<<age<<endl;
    }
};
int main()
{
    Kids_Teacher t1("小红",20);
    t1.show();     //小红 女 20
    t1.setName("小花");  //通过set接口重新设置姓名
    t1.show();     //小花 女 20
}

  1. 拷贝构造函数

概念:通过已存在的对象为新对象的数据成员完成初始化。

拷贝构造函数的参数是 对象的引用或者 const修饰的对象引用。它可以将已存在对象的属性值复制给新的对象中

特点:

拷贝构造函数是个特殊的函数,与构造函数构成重载

如果不显示给出拷贝构造函数,编译器会给出默认的构造函数,完成两个对象之间的值复制

对象之间是相互独立的的实体,数据也是相互独立

#include <iostream>
using namespace std;
class MobliePhone{
private:
    string brand;
    string model;
    int weight;
public:
    MobliePhone(string b,string m,int w):brand(b),model(m),weight(w){}
    void show(){
        cout<<"品牌:"<<brand<<" 型号:"<<model<<" 重量:"<<weight<<endl;
    }//不写出拷贝构造会给出与下方类似的默认拷贝构造
    MobliePhone(MobliePhone& other){
        brand=other.brand;
        model=other.model;
        weight=other.weight;
    }
    void getBrand(){
        cout<<&brand<<endl;
    }
};
int main()
{
    MobliePhone phone1("华为","P40",100);
    phone1.show();    MobliePhone phone2(phone1);
    phone2.show();    cout<<&phone1<<" "<< &phone2<<endl; //0x61fe74 0x61fe68
    phone1.getBrand(); //0x61fe74
    phone2.getBrand(); //0x61fe68}

4.深拷贝与浅拷贝

当对象中的属性有指针类型的时候,需要显示的写出构造函数

浅拷贝

定义:如果不显示的给出拷贝构造函数。会有默认的拷贝构造函数,完成对象之间简单的值赋值,简单值赋值的操作称为浅拷贝

如果对象中的属性有指针类型的时候,默认拷贝构造也只会简单的地址值的复制。导致两个对象的属性指向同一块内存。破坏了对象之间的独立性

    #include <iostream>
    #include <cstring>
    using namespace std;
    class MobliePhone{
    private:
        char * brand;
    public:
        MobliePhone(char * ch){
            brand=ch;
        }
        void show(){
            //cout后面如果是char *类型 ,它会自动打印出字符串//可以通过void * 强转,输出brand中存的字符串地址
            cout<<brand<<endl;
            cout<<(void *)brand<<endl;
        }
    };
    int main()
    {
        char a[10]="xiaomi";
        cout<<"字符串a的首地址:"<<(void *)a<<endl; //0x61fe86
        MobliePhone phone1(a);
        phone1.show();    //0x61fe86        MobliePhone phone2(phone1);
        phone2.show();   //0x61fe86        strcpy(a,"redmi");        phone1.show(); //0x61fe86
        phone2.show(); //0x61fe86    }

深拷贝

通过拷贝构造函数初始化新对象时。应该为新对象的指针类型的属性分配新的内存空间

#include <iostream>
#include <cstring>
using namespace std;
class MobliePhone{
private:
    char * brand;
public:
    MobliePhone(char * ch){
        //brand=ch;
        brand=new char[10]; //brand指向了新的一块内存
        strcpy(brand,ch); //把字符串的内容复制到brand指向的内存中
    }
    void show(){
        //cout后面如果是char *类型 ,它会自动打印出字符串
        //可以通过void * 强转,输出brand中存的字符串地址
        cout<<brand<<endl;
        cout<<(void *)brand<<endl;
    }
    MobliePhone(const MobliePhone& other){
        brand=new char[10]; //拷贝构造函数创建的对象的brand指向了新的一块内存
        strcpy(brand,other.brand); //只拷贝内容
    }};
int main()
{
    char a[10]="xiaomi";
    cout<<"字符串a的首地址:"<<(void *)a<<endl;
    MobliePhone phone1(a);
    phone1.show();    MobliePhone phone2(phone1);
    phone2.show();    strcpy(a,"redmi");    phone1.show();
    phone2.show();}

5.析构函数

对象生命期结束前应该完成对象资源的清理,这个工作由析构函数完成。比如创建对象时为数据成员开辟的空间,会通过析构函数在对象的生命期结束前进行释放。

若类中没有显式定义析构函数,则编译器会给出一个默认的析构函数,函数体为空,在对象生命期结束时默认的析构函数被执行。

默认析构函数形式:~类名(){ }

特点:

1.析构函数没有参数,所以不能重载。

2.对象销毁时自动调用

#include <iostream>
#include <cstring>
using namespace std;
class Cat{
private:
    string name;
public:
    Cat(string n):name(n){}
    ~Cat(){
      cout<<name<<"挂掉了"<<endl;
    }
};
void test(){
    Cat c1("tom");
    Cat * c2=new Cat("加菲"); //这时c2指向的内存不会自动销毁
}int main()
{
    test();
}

new关键字创建的堆内存对象需要手动delete销毁,才会自动调用析构函数

#include <iostream>
#include <cstring>
using namespace std;
class Cat{
private:
    string name;
public:
    Cat(string n):name(n){}
    ~Cat(){
      cout<<name<<"挂掉了"<<endl;
    }
};
void test(){
    Cat c1("tom");
    Cat * c2=new Cat("加菲");
    delete c2;
    c2=NULL;}
int main()
{
    test();
}

创建对象时如果为数据成员开辟了空间,需要通过析构函数在对象的生命期结束前进行释放

#include <iostream>
#include <cstring>
using namespace std;
class MobliePhone{
private:
    char * brand;
public:
    MobliePhone(char * ch){
        //brand=ch;
        brand=new char[10];
        strcpy(brand,ch);
    }
    void show(){        cout<<brand<<endl;
        cout<<(void *)brand<<endl;
    }
    MobliePhone(const MobliePhone& other){
        brand=new char[10];
        strcpy(brand,other.brand);
    }
    ~MobliePhone(){
        cout<<"析构函数调用"<<endl;
        delete [] brand;
    }
};
int main()
{
    {
        char a[10]="xiaomi";
        cout<<"字符串a的首地址:"<<(void *)a<<endl;
        MobliePhone phone1(a);
        phone1.show();        MobliePhone phone2(phone1);
        phone2.show();
    }
    cout<<"————————————————————"<<endl;}

6.作用域限定符::

6.1 名字空间

命名空间实际上是由程序设计者命名的内存区域,程序设计者可以根据需要指定一些有名字的空间区域,把一些自己定义的变量、函数等标识符存放在这个空间中,从而与其他实体定义分隔开来。

std是C++标准库的一个名字空间,很多使用的内容都是来自于标准名字空间,例如字符串std::string、std::cout...

当项目中包含using namespace std;时,代码中使用std名字空间中的内容就可以省略前面的std::

类似于不同班级可能有同名学生,可以加上班级名限定进行区分同名学生。

可以让不同的名字空间,存在相同的变量名和函数名。

#include <iostream>
using namespace std;
int a=20;
namespace mySpace {
    int a=30;
}
using namespace mySpace;
int main()
{
    int a=10;
    cout<<a<<endl;  //10
    cout<<::a<<endl; //20
    cout<<mySpace::a<<endl; //30}

6.2类内声明 内外定义 

当函数的声明和定义分离时,需要用到作用域限定符::指明函数是属于哪个范围的

#include <iostream>
using namespace std;
class Student{
private:
    string name;
    int age;
public:
    Student(string n,int a);
    string getName();
    int getAge();
    void setName(string n);
    void setAge(int a);
};
//实现
Student::Student(string n,int a){
    name=n;
    age=a;
}
string Student::getName(){
    return name;
}
int Student::getAge(){
    return age;
}
void Student::setName(string n){
    name=n;
}
void Student::setAge(int a){
    age=a;
}
int main()
{
    Student s("小明",20);
    cout<<s.getAge()<<" "<<s.getName()<<endl;}

7.explicit关键字

等号赋值时,等号的左边是对象类型,等号右侧恰好是构造函数可以接受的类型,这时编译器就会自动把等号右边的数据传入到构造函数中。相当于隐式调用了构造函数

#include <iostream>
using namespace std;
class Cow{
private:string name;
public:Cow(string n){
        name=n;}void show(){
        cout<<name<<endl;}};int main()
{Cow c1("小花");
   c1.show();string str="小白";Cow c2=str; //隐式调用构造函数
   c2.show();}

在对象操作过程中,可能不小心隐式调用了构造函数,造成错误。为了屏蔽隐式构造,可以加explicit关键字

#include <iostream>
using namespace std;
class Cow{
private:string name;
public:explicit Cow(string n){
        name=n;}void show(){
        cout<<name<<endl;}};
int main()
{Cow c1("小花");
   c1.show();string str="小白";//Cow c2=str; //不允许隐式构造
}

这篇关于2-C++的类与对象、封装、构造函数、拷贝、构析函数、作用域限定符的运用和explicit关键字的相关知识点的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/357960

相关文章

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

使用C/C++调用libcurl调试消息的方式

《使用C/C++调用libcurl调试消息的方式》在使用C/C++调用libcurl进行HTTP请求时,有时我们需要查看请求的/应答消息的内容(包括请求头和请求体)以方便调试,libcurl提供了多种... 目录1. libcurl 调试工具简介2. 输出请求消息使用 CURLOPT_VERBOSE使用 C

C++实现获取本机MAC地址与IP地址

《C++实现获取本机MAC地址与IP地址》这篇文章主要为大家详细介绍了C++实现获取本机MAC地址与IP地址的两种方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 实际工作中,项目上常常需要获取本机的IP地址和MAC地址,在此使用两种方案获取1.MFC中获取IP和MAC地址获取

C/C++通过IP获取局域网网卡MAC地址

《C/C++通过IP获取局域网网卡MAC地址》这篇文章主要为大家详细介绍了C++如何通过Win32API函数SendARP从IP地址获取局域网内网卡的MAC地址,感兴趣的小伙伴可以跟随小编一起学习一下... C/C++通过IP获取局域网网卡MAC地址通过win32 SendARP获取MAC地址代码#i