2-C++的类与对象、封装、构造函数、拷贝、构析函数、作用域限定符的运用和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; //不允许隐式构造
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值