C++学习四:类与对象的进阶

1.对象的权限

 在上一讲的类的基础中,你可能会认为类和C语言的结构体差不多,确实类和结构体在某一些地方很相似。两者有个很重要的区别,就是对象权限。

   权限的种类

  • public:成员可以在类的任何地方访问,也可以被派生类访问。
  • protected:成员可以在类的内部和派生类中访问。
  • private:成员只能在类的内部访问。

你可以认为结构体是权限为public的完全开放的类。 

2.封装

封装指的是,将类的一些属性和细节隐藏,重新提供外部访问接口,封装可以提升代码的安全性,并且可以让程序员更关注上层架构,而非内部细节。

class Phone
{
    private:
            float weight;//重量
            string brand;//手机品牌
            string model;//手机型号
    public:
           void phone_description()//描述手机
           { 
            cout<<weight<<" "<<brand<<" "<<model<<endl;
           }
            
}

这里将手机的属性也就是成员变量全部放到了private的权限下,类外的函数是无法调用这些成员变量的,那就没办法获取这些变量了吗。这里在public权限下提供了一个函数,可以调用这些变量而这个函数可以在类外使用,但是无法修改变量的值,只能显示变量的值,从而提高了安全性。

3.构造函数

基本使用

经过封装你可能有个疑问,既然如果提供的函数接口中没有给成员变量赋值的函数,那是怎么赋值的.

其实,当我们没有提供给成员变量赋值的接口,编译器会自动帮我们添加一个无参的构造函数.

构造函数是一种特殊的成员函数,用于创建对象时初始化。

写法要求:

  • 函数名称必须与类名完全相同。
  • 构造函数不写返回值。
构造函数支持有参数默认值,如果不初始化,则变量等于默认值;
class Phone
{
    private:
            float weight;//重量
            string brand;//手机品牌
            string model;//手机型号
    public:
           void phone_description()//描述手机
           { 
            cout<<weight<<" "<<brand<<" "<<model<<endl;
           }
           

           Phone(float w,string b,string m)//有参构造函数
           {
               weight=w;
               brand=b;
               model=m;
           }

           
           Phone(float w=3.2,string b="华为",string m="pro")//有参数默认值的构造函数
           {
               weight=w;
               brand=b;
               model=m;
           }
             
             
           
}

构造初始化列表

构造初始化列表是一种更简单的给成员变量赋予初始值的写法。

​
class Phone
{
    private:
            float weight;//重量
            string brand;//手机品牌
            string model;//手机型号
    public:
           void phone_description()//描述手机
           { 
            cout<<weight<<" "<<brand<<" "<<model<<endl;
           }
           

           Phone(float w,string b,string m)//有参构造函数
           {
               weight=w;
               brand=b;
               model=m;
           }

           
           Phone(float w=3.2,string b="华为",string m="pro")//有参数默认值的构造函数
           {
               weight=w;
               brand=b;
               model=m;
           }
            Phone(float w=3.2,string b="华为",string m="pro"):weight(w),brand(b),
            model(m)//构造初始化列表赋值
           {
               
           }
             
             
           
}

​

隐式调用构造函数

隐式调用调用指的是在创建对象时不写构造函数的名称与参数列表,编译器会尝试调用对应参数的构造函数。

可以使用explicit关键词在构造函数前缀,可以屏蔽隐式构造。

拷贝构造函数

拷贝构造函数就是用一个类的对象初始化赋值同一个类的对象。

分为浅拷贝和深拷贝,

浅拷贝:浅拷贝存在隐患,当成员变量存在指针类型时,会导致两个对象的成员变量指向同一个地址,不符合面向对象的规范

深拷贝:深拷贝使用new创建堆区空间,使得每次赋值时,每个对象都有自己的成员变量。深拷贝同样存在隐患,因为如果new开辟的内存空间需要主动释放,如果释放不及时,会导致内存泄漏。

析构函数

析构函数与构造函数正好相反,析构函数是用来销毁对象释放资源,比如new创建的对象可以用析构函数销毁释放资源。

构造函数

析构函数

创建对象时手动调用

当对象销毁时,自动调用

函数名称是类名

函数名称~类名

构造函数可以重载

析构函数没有参数,不能重载

用于创建对象时并初始化

用于销毁对象时释放资源

有返回值但是不写,返回值时新创建的对象

没有返回值

4.作用域限定符::

1.名字空间

名字空间通过将不同部分的代码组织到不同的命名空间下,使得开发者可以在同一个作用域中使用相同的标识符而不会产生冲突。

using namespace std;

我相信你一定见过这段代码,你可能不理解这段代码的作用,从英语翻译的角度,使用名字空间std,正因为这段代码你可以在接下来的代码中使用std名字空间的标识符,可以不用加std前缀;、

2.类内声明,类外定义

我们可以只在类内声明成员函数,然后在类外定义函数体,这样可以让我们的类看起来更简洁,在成员函数很复杂的情况下很好用。但是在类外定义时,需要加上类名::前缀来标识函数属于哪个类。

#include <iostream>
using namespace std;

class Demo
{
public:
    // 类内声明
    Demo();
    void test(string str);
};


Demo::Demo()
{
    cout << "构造函数" << endl;
}

void Demo::test(string str)
{
    cout << "string =" << str << endl;
}


int main()
{
    Demo d;
    d.test("test");
    return 0;
}

5.this指针

概念

this指针是一个特殊的函数指针,它指向当前类对象的首地址。成员函数中都有this指针,不用我们自己定义,但是不能在类外使用。

this指针的用法

1.在类内调用成员函数

this->成员变量/函数是一个非常常见且方便的用法。

#include <iostream>
using namespace std;

class Test
{
private:
    string name;
public:
    Test(string n)
    {
        // 编译器默认添加this指针指向当前对象
        this->name = n;
    }
    string get_name()
    {
       return this->name;
    }
};




2.区分同名的成员变量和局部变量

#include <iostream>
using namespace std;

class Test
{
private:
    string name;
public:
    Test(string name)//:name(name)    // 构造初始化列表区分
    {
        // 通过this指针在函数体中区分,this调用的name是成员变量,后面的name是参数即局部变量
        this->name = name;
    }
    string get_name()
    {
       return this->name;
    }
};

3.链式调用

链式调用就是在调用对象的成员函数时可以继续调用成员函数;

但是支持链式调用的成员函数必须要有以下特点:

1.成员函数的返回值类型时当前类的引用;

2.成员函数中的return *this

#include <iostream>
using namespace std;

class Test
{
private:
    int val = 0;
public:
    Test &add(int i)
    {
        val += i;   // val = val + i;
        return *this;
    }

    int get_val()
    {
        return val;
    }
};


int main()
{
    Test t1;
    t1.add(1);
    t1.add(2);
    t1.add(100);
    cout << t1.get_val() << endl;


    Test t2;
    // 链式调用
    cout << t2.add(2).add(32).add(200).get_val() << endl; // 234
    // cout << t2.get_val() << endl;    // 2

    return 0;
}

6.static关键字

1.静态局部变量

static修饰局部变量就是静态局部变量

特点:第一次调用时创建,只会初始化一次,直到程序结束后销毁,同一个类的所有对象共享相同的静态局部变量

2.静态成员变量

static修饰成员变量就是静态成员变量

特点:静态成员变量需要类内声明,类外初始化。

一个类的所有对象共用一份静态成员变量,虽然静态成员变量可以使用对象调用,但是更建议使用类名直接调用。静态成员变量可以脱离对象使用,在程序开始运行时就开辟内存空间直到程序结束后销毁。

3.静态成员函数

使用static修饰成员函数,这样的函数就是静态成员函数。

与静态成员变量相似的有:

  • 都可以通过类名直接调用,也可以通过对象调用(推荐使用类名直接调用)
  • 都可以脱离对象使用。

静态成员函数没有this指针,不能在静态成员函数中调用同类中其他非静态成员,但是静态成员函数可以调用静态成员

7.const关键字

1.const修饰局部变量

修饰的局部变量值不可以修改,常用于修饰参数。

2.const修饰成员变量

常成员变量的值,不可以修改。

3.const修饰成员函数

常成员函数,只能调用常成员函数,且不能修改成员变量的值

4.const修饰对象

const修饰的对象被称为常量对象,这种对象的成员变量无法被修改,只能调用常成员变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值