C++基础知识(四:类的学习)

本文详细介绍了C++中的类和结构体概念,包括类的定义、访问权限、类与结构体的区别,以及构造函数(包括默认构造、有参构造和初始化列表)、析构函数、拷贝构造函数、拷贝赋值函数和this指针的使用。讨论了深浅拷贝问题以及C++中类和结构体的不同点。
摘要由CSDN通过智能技术生成

目录

【1】定义

【2】类和结构体的区别

【3】this指针**

【4】类中的特殊成员函数

【5】构造函数

ii)构造函数提供了初始化列表的机制

iii)需要使用初始化列表的情况

【6】析构函数

i)格式

ii)调用时机

iii)需要显性写出析构函数的场景

【7】拷贝构造函数

i)格式

ii)使用

iii)深浅拷贝问题**

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

【8】拷贝赋值函数 

i)格式

ii)代码

【9】匿名对象

【10】C++中结构体和C的区别以及C++中结构体和类的区别


类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。

【1】定义

class 类名
{
访问权限:
    成员属性;
访问权限:
    成员方法;
}

访问权限:
public:共有的,类内、类外和子类中都可以访问
private:私有的,类内可以访问,类外和子类中都不能访问,类中默认是私有权限
protected:受保护的,类内和子类中可以访问,类外不可以访问(继承再说)

访问权限,是针对于一个类来说的

【2】类和结构体的区别

  1. 类的封装性比结构体更好,类中默认是私有权限,结构体中默认是共有权限
  2. 结构体默认是公有继承,类默认是私有继承
  3. C++中类就是由结构体演变来的
  4. 结构体一般用于实现某种数据结构时,类一般用于描述一类对象的性质

【3】this指针**

每一个类中的非静态成员函数,都有一个this指针,指向调用者,(非静态成员数隐藏 的形参)

谁调用this就指向谁哪一个类对象调用成员函数,就会用该类对象的首地址,初始化形参this

原型:类类型  *const this; ----->指针的指向不能修改

需要使用this指针的场合

  1. 当形参和成员属性同名
  2. 拷贝赋值函数,需要返回自身的引用
#include <iostream>

using namespace std;
class Rec
{
private:
    int len;
    int wid;
public:
    void set(int l,int w);  //设置长和宽
    void show();   //输出面积和周长
    int get_l();     //获取长和宽
    int get_wid();
};

void Rec::set(int len, int wid)   //r1.set()
{
    this->len = len;
    this->wid = wid;
}

void Rec::show()
{
    cout << "周长:" << (len+wid)*2 << endl;
    cout << "面积:" << len*wid << endl;
}

int Rec::get_l()
{
    return len;
}

int Rec::get_wid()
{
    return wid;
}
int main()
{
    Rec r1;  //r1是一个类对象,实例化了一个类对象r1
    r1.set(9,2);
    Rec r2;
    r2.set(9,2);
    cout << "&r1=" << &r1 << endl;
    return 0;
}

【4】类中的特殊成员函数

特殊的构造函数都是public权限

类中,会默认提供一些特殊的成员函数:构造函数、析构函数、拷贝构造函数、拷贝赋值函数

【5】构造函数

构造函数支持函数重载

构造函数,会在实例化类对象时,系统默认调用无参构造;

如果用户手动定义出了构造函数,系统将不再提供构造函数

类名()
{
   //函数体
}

调用时机

栈区:实例化类对象时,自动调用

堆区:什么时候使用new申请空间,什么时候调用构造函数

当提供构造函数后

ii)构造函数提供了初始化列表的机制

如果在函数体内部,给成员属性赋值,是一个赋值的过程,不是初始化的过程

类名():成员属性1(形参的值1),成员属性2(形参的值)`````

在函数头后面,使用:引出初始化列表,每个成员属性以,分隔,()里面是形参,外面是成员属性

iii)需要使用初始化列表的情况

  1. 形参和成员属性同名
  2. 类中有引用成员时,必须使用初始化列表
#include <iostream>

using namespace std;
class Stu
{
    int age;
    float high;
    int &score;
public:
    //构造函数支持函数重载
    Stu(int age,float high,int a):age(age),high(high),score(a)
    {
        cout << "Stu的有参构造" << endl;
    }
};

int main()
{
    //Stu s;
    int n1 = 90;
    Stu s1(18,9,n1);    //在栈区
    Stu *p;
    //p = new Stu;   //在堆区申请了Stu类对象的空间

    return 0;
}
  1. 类中有const修饰的成员时,必须使用初始化列表
#include <iostream>

using namespace std;
class Stu
{
    int age;
    float high;
    const int score;
public:
//    Stu()
//    {
//        cout << "Stu的无参构造函数" << endl;
//    }
    //构造函数支持函数重载
    Stu(int age,float high,int a):age(age),high(high),score(a)
    {
        cout << "Stu的有参构造" << endl;
    }
};

int main()
{
    //Stu s;
    int n1 = 90;
    Stu s1(18,9,89);    //在栈区
    Stu *p;
    //p = new Stu;   //在堆区申请了Stu类对象的空间

    return 0;
}
  1. 类中含有其他类的子对象时,必须使用初始化列表(类的包含关系)

(如果另一个类只有有参构造需要在初始化列表中宏显性调用,如果另一个类有无参构造,可以不写初始化列表)

                类的包含关系

#include <iostream>

using namespace std;
class Per
{
    string name;
public:
    Per(string name)
    {
        this->name = name;
        cout << "Per的有参构造" << endl;
    }
};

class Stu
{
    int age;
    float high;
    int score;
    Per p1;   //Per类只有有参构造函数
public:
    Stu():p1("zhangsan")
    {
        cout << "Stu的无参构造函数" << endl;
    }
    //构造函数支持函数重载
    Stu(int age,float high,int a,string name):p1(name)
    {
        this->age = age;
        this->high = high;
        this->score = a;
        cout << "Stu的有参构造" << endl;
    }
};

int main()
{
    Stu s;
    int n1 = 90;
    Stu s1(18,9,89,"lisi");    //在栈区

    return 0;
}

【6】析构函数

不支持函数重载

在类对象空间消亡时,系统自动调用

i)格式

~类名()
{
     //函数体
}

ii)调用时机

栈区:对象消亡时,自动调用
堆区:什么时候delete,什么时候调用

构造函数和析构函数调用的时机:

先构造的后析构,后构造的先析构

iii)需要显性写出析构函数的场景

类中有指针成员,并且指针成员,指向堆区的空间

#include <iostream>

using namespace std;
class Stu
{
    string name;
    int *p;
public:
    Stu():p(new int)   //保证指针成员,指向堆区的空间
    {
        cout << "堆区申请的空间为:" << p << endl;
        cout << "Stu的无参构造" << endl;
    }
//    Stu(string name,int p):name(name),p(new int(p))
//    {
//        cout << "Stu的有参构造" << endl;
//    }
    ~Stu()
    {
        cout << "准备释放堆区的空间:" << p << endl;
        delete p;
        cout << "Stu的析构函数" << endl;
    }

};

int main()
{
    Stu s1;
    return 0;
}

【7】拷贝构造函数

利用一个类对象,给另一个类对象初始化时,自动调用拷贝构造函数

如果自己实现了拷贝构造,系统不再提供默认的拷贝构造

i)格式

类名(同类对象的引用)
{
   //函数体
}

ii)使用

#include <iostream>

using namespace std;
class Per
{
    string name;
public:

    //Per中,自己定义了有参构造,系统不再提供无参构造
    Per(string name)
    {
        this->name = name;
        cout << "Per的有参构造" << endl;
    }
    ~Per()
    {
        cout << "Per的析构函数" << endl;
    }
};

class Stu
{
    int age;
    float high;
    int score;
    //Per p1;   //Per类只有有参构造函数
public:
    Stu()
    {
        cout << "Stu的无参构造函数" << endl;
    }
    //构造函数支持函数重载
    Stu(int age,float high)
    {
        this->age = age;
        this->high = high;
        cout << "Stu的有参构造" << endl;
    }

    //拷贝构造
    Stu(Stu &other)
    {
        //this->age = other.age;
        this->high = other.high;
        cout << "拷贝构造函数" << endl;
    }
    ~Stu()
    {
        cout << "Stu的析构函数" << endl;
    }
    void show();
};
void Stu::show()
{
    cout << "age = " << age << endl;
    cout << "high = " << high << endl;
}
int main()
{
    Stu s1(19,100);
    cout << "s1的show:" << endl;
    s1.show();
    Stu s2=s1;
    cout << "s2的show:" << endl;
    s2.show();
    return 0;
}

iii)深浅拷贝问题**

当类中有指针成员,会涉及到深浅拷贝问题

浅拷贝:两个不同类对象的指针成员,指向同一片空间

问题:析构时,会发生二次释放问题;同一片空间被两个不同的类对象占用,发生资源抢占

深拷贝:两个不同类对象的指针成员,指向不同的空间,但是保存的是同样的数据

浅拷贝

深拷贝

#include <iostream>

using namespace std;
class Stu
{
    string name;
    int *p;
public:
    Stu():p(new int)   //保证指针成员,指向堆区的空间
    {
        cout << "堆区申请的空间为:" << p << endl;
        cout << "Stu的无参构造" << endl;
    }
    Stu(string name,int p):name(name),p(new int(p))
    {
    }
    ~Stu()
    {
        cout << "准备释放堆区的空间:" << p << endl;
        delete p;

        cout << "Stu的析构函数" << endl;
    }

    //使用同类其他对象的指针成员解引用后的值,给自己的指针成员的内容初始化
    Stu(Stu &other):name(other.name),p(new int(*(other.p)))
    {
        /*this->name = other.name;
        this->p = new int(*(other.p));*/
        cout << "Stu的拷贝构造" << endl;
    }
    void show();
};
void Stu::show()
{
    cout << "name= " << name << endl;
    cout << "p= " << p << endl;
    cout << "*p= " << *<< endl;
}
int main()
{
    Stu s1("zhangsan",18);
    cout << "s1的show:" << endl;
    s1.show();
    Stu s2 = s1;
    cout << "s2的show:" << endl;
    s2.show();
    return 0;
}

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

  1. 使用已有的类对象,给新的类对象初始化
  2. 函数的参数是一个类对象时,也会调用拷贝构造函数
  3. 函数的返回值是一个类对象时,也会调用拷贝构造函数

测试代码:

#include <iostream>

using namespace std;
class Stu
{
    string name;
public:
    Stu()
    {
        cout << "Stu的无参构造" << endl;
    }
    Stu(Stu &other):name(other.name)
    {
        cout << "Stu的拷贝构造函数" << endl;
    }
    Stu(string name):name(name)
    {
        cout << "Stu的右参构造" << endl;
    }
};

Stu fun(Stu s1)
{
    return s1;
}
int main()
{
    Stu s;
    //Stu s2(fun(s));   //会报错,因为fun(s)的返回值是一个临时值,不能引用
    return 0;
}

【8】拷贝赋值函数 

使用已有的类对象,给另外一个已有的类对象赋值

系统默认提供一个拷贝赋值函数

本质:赋值运算符的重载

i)格式

类名 &operator=(const 类名&other)
{
  //函数体
}

ii)代码

 #include <iostream>

using namespace std;
class Stu
{
    string name;
    int *p;
public:

    //深拷贝赋值函数
    Stu &operator=(const Stu &other)
    {
        name = other.name;
        *= *(other.p);
        cout << "Stu的拷贝赋值函数" << endl;
        return *this;
    }
    Stu():p(new int)   //保证指针成员,指向堆区的空间
    {
        cout << "堆区申请的空间为:" << p << endl;
        cout << "Stu的无参构造" << endl;
    }
    Stu(string name,int p):name(name),p(new int(p))
    {
    }
    ~Stu()
    {
        cout << "准备释放堆区的空间:" << p << endl;
        delete p;

        cout << "Stu的析构函数" << endl;
    }

    //使用同类其他对象的指针成员解引用后的值,给自己的指针成员的内容初始化
    Stu(Stu &other):name(other.name),p(new int(*(other.p)))
    {
        /*this->name = other.name;
        this->p = new int(*(other.p));*/
        cout << "Stu的拷贝构造" << endl;
    }
    void show();
};
void Stu::show()
{
    cout << "name= " << name << endl;
    cout << "p= " << p << endl;
    cout << "*p= " << *<< endl;
}
int main()
{
    Stu s1("zhangsan",18);
    cout << "s1的show:" << endl;
    s1.show();
    Stu s2;
    s2 = s1;
    cout << "s2的show:" << endl;
    s2.show();
    return 0;
}


【9】匿名对象

没有对象名,通过类名实例化出来的对象,类名();

Stu();生命周期更短

  1. 全局函数传参
  2. 类数组赋值     //int a=9,b=7,c=8;    int arr[3]={a,b,c};   //int arr[3]={9,7,8};
  3. 临时调用类中的成员函数
  4. 给新的类对象赋值
#include <iostream>

using namespace std;
class Stu
{
    string name;
    int *p;
public:

    //深拷贝赋值函数
    Stu &operator=(const Stu &other)
    {
        name = other.name;
        *= *(other.p);
        cout << "Stu的拷贝赋值函数" << endl;
        return *this;
    }
    Stu():p(new int)   //保证指针成员,指向堆区的空间
    {
        cout << "堆区申请的空间为:" << p << endl;
        cout << "Stu的无参构造" << endl;
    }
    Stu(string name,int p):name(name),p(new int(p))
    {
    }
    ~Stu()
    {
        cout << "准备释放堆区的空间:" << p << endl;
        delete p;

        cout << "Stu的析构函数" << endl;
    }

    //使用同类其他对象的指针成员解引用后的值,给自己的指针成员的内容初始化
    Stu(const Stu &other):name(other.name),p(new int(*(other.p)))
    {
        /*this->name = other.name;
        this->p = new int(*(other.p));*/
        cout << "Stu的拷贝构造" << endl;
    }
    void show();
};
void Stu::show()
{
    cout << "name= " << name << endl;
    cout << "p= " << p << endl;
    cout << "*p= " << *<< endl;
}

void fun(Stu s1)
{
    cout << "调用成功" << endl;
}
int main()
{
    //1、使用匿名对象用做全局函数传参
    fun(Stu());   //匿名对象的生命周期,只在定义语句的位置,是一个临时值
    
    //2、想要临时使用类中的成员函数
    Stu().show();

    //3、给类对象的数组赋值
    Stu arr[3]={Stu("zhangsan",8),Stu("lisi",19),Stu("xiaoming",20)};

    //4、给新的类对象赋值
    Stu s3(Stu("zhangsan",18));
    return 0;
}

【10】C++中结构体和C的区别以及C++中结构体和类的区别

  1. C中定义需要加struct,C++中可以不加struct
  2. C++中结构体可以有访问权限的控制(public、private、protected)
  3. C++中结构体可以继承
  4. C++中结构体可以封装函数
  5. C++中结构体内可以定义另外一个结构体声明(类型)

结构体和类的区别:

  1. 使用场合不同,类适用于某一类对象属性和方法的封装,结构体用于某种数据结构的实现
  2. 类中默认private,结构体中默认是public
  3. 类默认是私有继承,结构体默认是共有继承
  4. 类的封装性比结构体的封装性更好
  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

笨笨小乌龟11

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

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

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

打赏作者

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

抵扣说明:

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

余额充值