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

相关文章

javaSE类和对象进阶用法举例详解

《javaSE类和对象进阶用法举例详解》JavaSE的面向对象编程是软件开发中的基石,它通过类和对象的概念,实现了代码的模块化、可复用性和灵活性,:本文主要介绍javaSE类和对象进阶用法的相关资... 目录前言一、封装1.访问限定符2.包2.1包的概念2.2导入包2.3自定义包2.4常见的包二、stati

Django中的函数视图和类视图以及路由的定义方式

《Django中的函数视图和类视图以及路由的定义方式》Django视图分函数视图和类视图,前者用函数处理请求,后者继承View类定义方法,路由使用path()、re_path()或url(),通过in... 目录函数视图类视图路由总路由函数视图的路由类视图定义路由总结Django允许接收的请求方法http

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

python运用requests模拟浏览器发送请求过程

《python运用requests模拟浏览器发送请求过程》模拟浏览器请求可选用requests处理静态内容,selenium应对动态页面,playwright支持高级自动化,设置代理和超时参数,根据需... 目录使用requests库模拟浏览器请求使用selenium自动化浏览器操作使用playwright

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

postgresql使用UUID函数的方法

《postgresql使用UUID函数的方法》本文给大家介绍postgresql使用UUID函数的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录PostgreSQL有两种生成uuid的方法。可以先通过sql查看是否已安装扩展函数,和可以安装的扩展函数