C++快速讲解(五):类和对象


前言:主要介绍了对象的创建、访问修饰符、实现类的成员函数、构造函数、析构函数、拷贝构造函数、移动构造函数、常函数和常对象、静态变量和函数、友元等。


1.类和对象

类是构成对象的一个蓝图、可以拥于属性(用于表示数据)、可以拥有方法 ( 用于表示行为动作 )、可以隐藏数据和方法、可以对外提供公开的接口

1.1 在栈中创建对象

#include <iostream>
#include <string>

using namespace std;

//创建一个类
class stu{
private://私用的属性
    string name;
    int age;

public://公共的方法
    void read(){
        cout<<"look book"<<endl;
    }
};
int main() {
    stu s1; //在栈中创建对象
    s1.read();//访问read函数
    stu s2;//在栈中创建对象
    s2.read();
    return 0;
}

1.2 在堆中创建对象

#include <iostream>
#include <string>

using namespace std;

//创建一个类
class stu{
private://私用的属性
    string name;
    int age;

public://公共的方法
    void read(){
        cout<<"look book"<<endl;
    }
};
int main() {
    stu *s3 = new stu;//在堆中创建对象
    s3->read();//访问对象的方法
//    *s3->read();//另一种访问方法
    delete s3;//进行回收内存
    return 0;
}

2.访问修饰符

  • public : 公开权限,任何位置都可以访问
  • private : 私有权限,只能自己内部访问及其友元函数
  • protected : 类内、子类及友元函数可访问
#include <iostream>
#include <string>

using namespace std;

//创建一个类
class stu{
private://私用的属性
    string name;
public://公共的方法
    int age;
    void run(){
        cout<<"run:----"<<age<<endl;
    }
};
int main() {
    stu s1; //在栈中创建对象
//    s1.name = "fly";//不能访问,因为是private
    s1.age = 18;
    s1.run();//可以访问

    return 0;
}

在这里插入图片描述

3.实现类的成员函数

3.1 实现

#include <iostream>
#include <string>

using namespace std;

//创建一个类
class stu{
private://私用的属性
    string name;
    int age;
public://公共的方法

    void read(string bookname){
        cout<<"bookname:----"<<bookname<<endl;
    }

    void speak(string str);
};

//在类的外部实现函数
void stu::speak(string str) {
    cout<<"out-speak--"<<str<<endl;
}

int main() {
    stu s1; //在栈中创建对象
    s1.read("san ti");
    s1.speak("i love move");

    return 0;
}

在这里插入图片描述

3.2 分离实现

stu.h

//
// Created by Fly on 2021/3/21.
//

#ifndef DAY04_STU_H
#define DAY04_STU_H
#include <string>

using namespace std;
class Student{
private:
    int age;
    string name;

public:
    void read(string name);
    void speak(string something);
};
#endif //DAY04_STU_H

stu.cpp

//
// Created by Fly on 2021/3/21.
//

#include "stu.h"
#include <iostream>

using namespace std;

void Student::read(string name) {
    cout<<"name"<<name<<endl;
}

void Student::speak(string something) {
    cout<<"something"<<something<<endl;
}

main代码

#include <iostream>
#include "stu.h"

int main() {

    Student s;
    s.read("三体");
    s.speak("你好!");
    return 0;
}

在这里插入图片描述

4.构造函数

构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。与类名同名,没有返回值,可以被重载,通常用来做初始化工作。

4.1 一般方式构造

#include <iostream>
#include <string>

using namespace std;
//一般方式构造

class stu{

    string name;
    int age;

public:
    //构造函数
    stu(){
        cout<<"无参数构造函数"<<endl;
    }

    //有参构造
    stu(string val_name){
        cout<<"一个参数构造函数"<<endl;
        name = val_name;//赋值给类的name
    }

    //有两个参数的构造函数
    stu(string val_name,int val_age){
        cout<<"两个参数构造函数"<<endl;
        name = val_name;//赋值给类的name
        age = val_age;//赋值给类的age
    }

    void run(){
        cout<<"name"<<name<<endl;
        cout<<"age"<<age<<endl;
    }
};
int main() {
    stu s1;
    s1.run();
    stu s2("fly");
    s2.run();
    stu s3 ("张三",28);
    s3.run();

    return 0;
}

在这里插入图片描述

4.2 初始化列表方式

#include <iostream>
#include <string>

using namespace std;

class stu{

    string name;
    int age;

public:
    //构造函数
    stu(){
        cout<<"无参数构造函数"<<endl;
    }

    //初始化列表方法
    stu(string name,int age):name{name},age{age}{
        cout<<"两个参数构造函数"<<endl;
    }

    void run(){
        cout<<"name"<<name<<endl;
        cout<<"age"<<age<<endl;
    }
    
};
int main() {
    stu s3("张三",28);
    s3.run();
    return 0;
}

在这里插入图片描述

4.3 委托构造函数

#include <iostream>
#include <string>

using namespace std;


class stu{

    string name;
    int age;

public:
    //构造函数
    stu():stu("fly",26){
        cout<<"无参数构造函数"<<endl;
    }

    //构造函数
    stu(string name):stu(name,26){
        cout<<"一个参数构造函数"<<endl;
    }

    stu(string name,int age):name{name},age{age}{
        cout<<"两个参数构造函数"<<endl;
    }

};
int main() {
    stu s1;
    stu s2("fly");
    stu s3("fly", 26);

    return 0;
}

在这里插入图片描述

4.4 this构造

#include <iostream>
#include <string>

using namespace std;

class Student{
public:
    int age;
    string name;

    Student(int age, string name){
        this->name = name;
        this->age = age;
    }

    void runName(){
        cout<<"age:"<<age<<endl;
    }

    void runAge(){
        cout<<"name:"<<name<<endl;
    }

};
int main(){
    Student s1(18,"fly");
    s1.runAge();
    s1.runName();
    return 0;
}


5.析构函数

类的析构函数是类的一种特殊的成员函数,与构造函数正好相反,它会在每次删除所创建的对象时执行。一般用于释放资源。

#include <iostream>
#include <string>

using namespace std;
//析构函数
//类的析构函数是类的一种特殊的成员函数,与构造函数正好相反,它会在每次删除所创建的对象时执行。


class stu{

    string name;
    int age;

public:
    //构造函数
    stu(){
        cout<<"无参数构造函数"<<endl;
    }

    //构造函数
    stu(string name){
        cout<<"一个参数构造函数"<<endl;
    }

    stu(string name,int age){
        cout<<"两个参数构造函数"<<endl;
    }

    //析构函数
    ~stu(){
        cout<<"执行析构函数"<<endl;
    }


};
int main() {
    stu *s1 = new stu;
    stu *s2 = new stu("fly");
    stu *s3 = new stu("fly",26);

    delete s1;
    delete s2;
    delete s3;

    return 0;
}

在这里插入图片描述

6.拷贝构造函数

6.1 引出浅拷贝

对象的复制

#include <iostream>
#include <string>

using namespace std;
//认识拷贝


class stu{

public:
    string name;
    int age;

    //有两个参数的构造函数
    stu(string name,int age): name{name},age{age}{
        cout<<"两个参数构造函数"<<endl;
    }

    ~stu(){
        cout<<"执行析构函数"<<endl;
    }


};
int main() {
    stu s1("fly",26);
    cout << s1.name << " : " << s1.age <<endl;
    cout <<"s1.name的地址"<< &s1.name<<endl;
    stu s2 = s1;
    cout << s2.name << " : " << s2.age <<endl;
    cout <<"s2.name的地址"<< &s2.name<<endl;

    return 0;
}

在这里插入图片描述
从运行结果我们可以看出对象的复制不是简单的地址的复制,是复制了另一个的数据,然后重新建立一个储存空间(地址不一样可以得出),这个就是类里面拷贝构造函数的实现。

6.2 浅拷贝

#include <iostream>
#include <string>

using namespace std;
//浅拷贝
//指的是在对象复制时,只对对象中的数据成员进行简单的赋值,
// 默认拷贝构造函数执行的也是浅拷贝。
// 如果数据中有属于动态成员 ( 在堆内存存放 ) ,那么浅拷贝只是做指向而已,不会开辟新的空间。默认情况下,编译器提供的拷贝操作即是浅拷贝。


class stu{

public:
    string name;
    int age;

    //有两个参数的构造函数
    stu(string name,int age): name{name},age{age}{
        cout<<"两个参数构造函数"<<endl;
    }

    //拷贝构造函数
    stu(const stu &s){
        cout<<"拷贝函数"<<endl;
        name = s.name;
        age = s.age;
    }

    ~stu(){
        cout<<"执行析构函数"<<endl;
    }


};
int main() {
    stu s1("fly",26);
    cout << s1.name << " : " << s1.age <<endl;
    cout <<"s1.name的地址"<< &s1.name<<endl;
    stu s2 = s1;
    cout << s2.name << " : " << s2.age <<endl;
    cout <<"s2.name的地址"<< &s2.name<<endl;

    return 0;
}

在这里插入图片描述

6.3 浅拷贝带来的问题

默认情况下,浅拷贝已经足以应付日常的需求了,但是当类中的成员存在动态成员(指针)时,浅拷贝往往会出现一些奇怪的问题。

#include <iostream>
#include <string>

using namespace std;
//浅拷贝引发的问题

class stu{

public:
    string name;
    string *address = nullptr;

    //有两个参数的构造函数
    stu(string name,string *address): name{name},address{address}{
        cout<<"两个参数构造函数"<<endl;
    }

    //拷贝构造函数
    stu(const stu &s){
        cout<<"拷贝函数"<<endl;
        name = s.name;
        address = s.address;
    }

    ~stu(){
        cout<<"执行析构函数"<<endl;
        //这里将会删除两次内存空间
        delete address;
        address = nullptr;
    }


};
int main() {

    string address = "shenzhen";
    stu s1("fly",&address);
    cout << "s1.name:"<<s1.name <<"  &s1.name:" <<&s1.name<< "  *s1.address:" << *s1.address <<"  s1.address:"<<s1.address <<endl;

    //执行拷贝
    stu s2 = s1;
    cout << "s2.name:"<<s2.name <<"  &s2.name:" <<&s2.name<< "  *s2.address:" << *s2.address <<"  s2.address:"<<s2.address <<endl;


    //修改第一个学生的地址,第二个也会跟着变化
    *s1.address = "beijing";
    cout << "s1.name:"<<s1.name <<"  &s1.name:" <<&s1.name<< "  *s1.address:" << *s1.address <<"  s1.address:"<<s1.address <<endl;
    cout << "s2.name:"<<s2.name <<"  &s2.name:" <<&s2.name<< "  *s2.address:" << *s2.address <<"  s2.address:"<<s2.address <<endl;

    return 0;
}

在这里插入图片描述
从这里我们可以看出,执行系统提供的浅拷贝的时候,当类中的成员存在动态成员(指针)时,浅拷贝就会失效,这里修改第一个学生的地址,第二个也会跟着变化,所以可以得出结论,浅拷贝的时候,只是把地址拷贝了一份,两个地址指向同一个储存空间,所以我们需要深拷贝来解决这个问题。

6.4 深拷贝

为了解决当类中的成员存在动态成员(指针)时,浅拷贝就会失效的问题,我们需要自己实现 深拷贝,来解决这一问题。

#include <iostream>
#include <string>

using namespace std;
//深拷贝


class stu{

public:
    string name;
    string *address = nullptr;

    //有两个参数的构造函数
    stu(string name,string *address): name{name},address{address}{
        cout<<"两个参数构造函数"<<endl;
    }

    //拷贝构造函数
    stu(const stu &s){
        cout<<"拷贝函数"<<endl;
        name = s.name;
        //实现深拷贝
        if (address == nullptr){
            address = new string(*s.address) ;
        }
    }

    ~stu(){
        cout<<"执行析构函数"<<endl;
        //这里将会删除两次内存空间
        delete address;
        address = nullptr;
    }


};

int main() {

    string address = "shenzhen";
    stu s1("fly",&address);
    cout << "s1.name:"<<s1.name <<"  &s1.name:" <<&s1.name<< "  *s1.address:" << *s1.address <<"  s1.address:"<<s1.address <<endl;

    //执行拷贝
    stu s2 = s1;
    cout << "s2.name:"<<s2.name <<"  &s2.name:" <<&s2.name<< "  *s2.address:" << *s2.address <<"  s2.address:"<<s2.address <<endl;


    //修改第一个学生的地址,这时候第二个不会变化
    *s1.address = "beijing";
    cout << "s1.name:"<<s1.name <<"  &s1.name:" <<&s1.name<< "  *s1.address:" << *s1.address <<"  s1.address:"<<s1.address <<endl;
    cout << "s2.name:"<<s2.name <<"  &s2.name:" <<&s2.name<< "  *s2.address:" << *s2.address <<"  s2.address:"<<s2.address <<endl;

    return 0;
}

在这里插入图片描述

6.5 触发拷贝的场景

#include <iostream>
#include <string>

using namespace std;
//触发拷贝的场景


class stu{

public:
    string name;
    string address;

    //有两个参数的构造函数
    stu(string name,string address): name{name},address{address}{
        cout<<"两个参数构造函数"<<endl;
    }

    //拷贝构造函数
    stu(const stu &s){
        cout<<"拷贝函数"<<endl;
        name = s.name;
        address = s.address;
    }

    ~stu(){
        cout<<"执行析构函数"<<endl;
    }


};

void printStu(stu s){

    cout << s.name << " : "<< s.address << endl;
}

stu createStu(){
    return stu("张三","beijing");
}

int main() {

    //对象创建依赖于其他对象
    cout << "---对象创建依赖于其他对象---" << endl;
    stu s1("张三","shenzhen"); //执行构造函数
    stu s2 = s1; //执行拷贝构造函数
    cout << "------" << endl;

    //函数参数
    cout << "---函数参数---" << endl;
    stu s3("fly","changsha"); //执行构造函数
    printStu(s3); //执行拷贝构造函数
    cout << "------" << endl;

    //函数返回值
    cout << "---函数返回值---" << endl;
    stu s4= createStu();
    cout << "------" << endl;
    return 0;
}

在这里插入图片描述

7.移动构造函数

使用移动构造,会使得在返回对象时,不会调用拷贝构造函数,这也就避免产生了对象的拷贝工作。

#include <iostream>
#include <string>

using namespace std;
//使用移动构造


class Student{
public :
    int *age;
    Student():age(new int(18)){
        cout << "执行构造函数~!~"<<endl;
    }

    //深拷贝
    Student(const Student &s):age(new int(*s.age)){
        cout << "执行拷贝构造函数~!~"<<endl;
    }

    //移动构造
    Student( Student &&s):age(s.age){
        cout << "执行移动!!!构造函数~!~"<<endl;
        //移动之后,一般即可让原有对象的指针变成空指针
        s.age = nullptr;

    }

    //析构
    ~Student(){
        cout <<"执行析构函数~!" << endl;
        delete  age;
    }
};

Student getStu(){
    Student s ;
    cout <<"getTemp =" << __func__ << " : " << hex << s.age << endl;
    return s;
}



int main() {
    Student stu = getStu();
    cout <<"stu.age:" << stu.age << endl;
    Student stu3 = move(stu);
    cout <<"stu3.age:" << stu3.age << endl;
    cout <<"stu.age:" << stu.age << endl;
    return 0 ;
}

在这里插入图片描述

8.this 指针

this只能在成员函数中使用。全局函数,静态函数都不能使用this。全局函数不属于任何一个类,静态函数也不依赖任何一个类,所以他们都不会有this指针 。 在外部看来,每一个对象都拥有自己的成员函数,一般情况下不写this,而是让系统进行默认配置。this指针永远指向当前对象 , 所以成员函数中的,通过this即可知道操作的是哪个对象的数据。

#include <iostream>
#include <string>

using namespace std;

class Student{
public:
    int age;
    string name;

    Student(int age, string name){
        this->name = name;
        this->age = age;
    }

    Student &getStu(){
        return *this;
    }

    void runName(){
        cout<<"age:"<<age<<endl;
    }

    void runAge(){
        cout<<"name:"<<name<<endl;
    }

};
int main(){
    Student s1(18,"fly");
    Student &s2 = s1.getStu(); //使用引用来接收,表示指向同一个引用,
    s2.runAge();
    s2.runName();
    return 0;
}


在这里插入图片描述

9.常函数和常对象

  • 如果确定了某个成员函数不会修改成员变量,那么可以把该函数声明为常函数。函数体内不允许修改成员变量,除非该变量使用mutable修饰。
  • 常对象其实也就是一个变量,和以前的常量没有相差多少。不同的是如今对象里面可以能包含很多的变量,以前的常量指的只是一个变量而已。 若类中有变量使用mutable修饰,那么该变量在常对象状态下,可以被修改。
#include <iostream>
#include <string>

using namespace std;

class  Student{

public:
    string name  = "无名氏";
    int age  = 18; //除非该变量使用mutable修饰.

    Student(string name,int age){
        this->name = name;
        this->age = age;
    }

    //在此添加 const 即可变成常函数,
    void printStudent() const{
        // age = 10 ;  错误,不允许修改
        cout << name  << "  "  << age << endl;
    }

};
int main(){
    const Student s1 ("zhangsan " ,18);

    cout << s1.name << endl; //访问常对象中的成员 ,OK
//    s1.name = "李四" ; // 试图修改常对象中的成员 , error
    return 0;
}


10.静态变量和函数

#include <iostream>
#include <string>

using namespace std;

class Student{

public:
    int age ;
    string name;
    static string school; //静态成员
    static string getSchool(){//静态函数
        //不能访问 name 和 age
        return school;
    }

};

//在类的外面对类的静态成员进行初始化。
string Student::school = "beijing un";

int main(){
    Student s ;
    cout << s.getSchool() << endl; //可以访问

    //也可以使用类名的方式访问
    cout << Student::school <<endl;
    return 0 ;
}


在这里插入图片描述
注意:

  • 静态成员属于类,不属于对象
  • 静态成员变量必须在类中声明,在类的外部初始化
  • 静态成员变量的声明周期是整个程序,作用域在类的内部
  • 静态成员函数只能访问静态成员变量
  • 静态成员可以使用类来访问,也可以使用对象来访问。

11.友元

11.1 友元函数

#include <iostream>
#include <string>

using namespace std;
//友元函数

class Student{
private:
    string name = "fly";
    friend void showName(Student s);

};

void showName(Student s){
    cout<<s.name<<endl;
}

int main(){
    Student s;
    showName(s);
    return 0 ;
}

在这里插入图片描述

11.2 友元类

#include <iostream>
#include <string>

using namespace std;
//友元类

class Student{
private:
    string name = "fly";
    friend class S2;

public:
    string getName(){
        return name;
    }
};

class S2{
public:
    void changeName(Student &s){
        s.name = "wang";
    }
};

int main(){

    Student s;
    S2 s2;

    s2.changeName(s);
    cout<<s.getName()<<endl;

    return 0 ;
}

在这里插入图片描述


结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

等待着冬天的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值