第五篇 C++关于类和继承那些事

1:C++类中的几种修饰符有啥作用?

答: C++中的类修饰符主要用于控制类成员的访问权限和特性。以下是几种常见的类修饰符及其作用:

public

  • 公有修饰符,指定的类成员可以在类的内部和外部访问。
    在类的外部,公有成员可以通过对象的实例访问。

private

  • 私有修饰符,指定的类成员只能在类的内部访问,外部无法直接访问。
    私有成员通常用于隐藏类的实现细节,只允许通过类的公共接口来访问。

protected

  • 保护修饰符,指定的类成员可以在类的内部访问,并且对派生类可见。
    受保护的成员对于类的外部是不可见的,但对于派生类是可访问的。

static

  • 静态修饰符,用于声明静态成员变量或静态成员函数。
    静态成员变量被所有类对象所共享,而静态成员函数可以直接通过类名调用,无需创建对象。

const

  • 常量修饰符,用于指示成员变量是常量,其值在初始化后不可更改。
    对于成员函数,const修饰符表示该函数不会修改对象的状态。

virtual

  • 虚拟修饰符,用于定义虚函数,实现多态性。
    虚函数可以在派生类中重写(覆盖),并根据对象的实际类型来调用相应的函数。
    这些类修饰符可以帮助您控制类中成员的访问权限、共享性质以及特殊行为,是面向对象编程中重要的概念之一。

2:静态函数与静态变量在类中怎么使用?

答:在 C++ 中,静态函数和静态变量可以在类中使用,并且它们属于整个类而不是类的实例。静态函数和静态变量可以通过类名直接访问,而不需要创建类的对象实例。

静态成员函数

  • 静态成员函数是属于类本身的函数,它们不依赖于类的实例,因此可以被类名直接调用。静态成员函数没有 this 指针,因此无法访问非静态成员变量和非静态成员函数。

静态成员变量

  • 静态成员变量是属于整个类的变量,所有实例共享同一份静态成员变量。静态成员变量必须在类外部进行定义和初始化。

3:友元函数有什么作用

答:友元函数在 C++ 中是一种特殊的机制,允许某个函数或类访问另一个类的私有成员。友元函数的作用包括以下几点:

访问私有成员:

友元函数可以直接访问类的私有成员变量和私有成员函数,无需通过类的公共接口。
这样可以在需要时提供对类私有部分的访问权限,但同时也可能破坏封装性,需谨慎使用。

增强类与函数的关联性:

友元函数可以增强类与函数之间的关联性,使得某些函数能够更自然地操作类的私有数据。
有些情况下,类中的数据需要被某个外部函数访问或操作,这时可以考虑使用友元函数。

提高效率:

在某些情况下,使用友元函数可以避免通过公共接口频繁调用函数,提高程序的执行效率。
如果某个函数需要频繁访问类的私有数据,并且这个函数并不适合成为类的成员函数,可以考虑将其声明为友元函数。在使用友元函数时,需要注意以下几点:

  • 友元函数的声明通常放在类的声明中,但定义则不属于类的成员函数
  • 友元关系不能被继承
  • 友元关系是单向的,即如果类A是类B的友元,类B不会成为类A的友元
  • 友元函数不是类的成员函数,因此不能直接访问 this 指针。

总的来说,友元函数是一种灵活的权限控制机制,可以在需要时候打破类的封装性,使得外部函数能够更方便地访问类的私有部分。然而,过度地使用友元函数可能会降低代码的可维护性和封装性,因此应该根据具体情况谨慎使用。

4:C++中几种继承关系怎么处理?

答:在C++中,继承关系是面向对象编程的重要概念之一,主要有三种类型的继承关系:公有继承、保护继承和私有继承。下面分别介绍这三种继承关系的处理方法:
  1. 公有继承(public inheritance)
    公有继承是最常见的一种继承方式,子类(派生类)继承父类(基类)的公有成员和保护成员,并可以访问父类的公有成员。

  2. 保护继承(protected inheritance)
    保护继承会导致父类的公有成员在子类中变为保护成员,而父类的保护成员在子类中保持不变。

  3. 私有继承(private inheritance)
    私有继承会导致父类的公有和保护成员在子类中变为私有成员

5: 虚继承有什么作用 ?

答: 在C++中,虚继承是一种特殊的继承方式,用于解决菱形继承(diamond inheritance)所带来的问题。虚继承可以有效地解决多重继承中出现的二义性和资源浪费的情况。

6:虚函数和纯虚函数有什么作用

答:虚函数和纯虚函数是 C++ 中用于实现多态的重要概念,它们都可以在继承关系中发挥关键作用。

虚函数:

  • 虚函数是在基类中使用 virtual 关键字声明的成员函数

  • 当在派生类中重新定义(override)这个虚函数时,可以实现运行时多态(动态绑定)。

  • 在使用基类指针或引用指向派生类对象时,通过虚函数实现的多态性可以确保调用正确的派生类版本的函数。

  • 虚函数使得程序更容易扩展和维护,同时也促进了代码的灵活性和可重用性。

纯虚函数:

  • 纯虚函数是在基类中没有具体实现的虚函数,只有声明,格式为 virtual returnType functionName() = 0;

  • 包含纯虚函数的类称为抽象类,无法创建该类的实例,只能作为基类用于派生其他类。

  • 派生类必须实现所有基类中的纯虚函数才能成为具体类,否则仍然是抽象类。

  • 纯虚函数提供了一种接口规范,强制子类实现特定的方法,从而实现接口的统一性并确保类的一致性。

总结来说,虚函数通过运行时多态实现动态绑定,允许在继承关系中根据对象的实际类型调用正确的函数;而纯虚函数用于定义接口规范和实现基类的骨架,强制子类提供特定的实现。这两种函数类型都是实现面向对象设计中重要的概念,有助于提高代码的可扩展性、灵活性和可维护性。

7:多重继承时的能不能通过某一个父类实例化子类?

答:在 C++ 中,多重继承是指一个类同时继承自多个父类。当使用多重继承时,通过某一个父类实例化子类是不合法的,因为在多重继承中,子类具有多个父类,需要在构造子类对象时调用所有父类的构造函数。在实例化子类时,编译器会要求你提供每个父类所需的参数,而不是只提供其中一个父类的参数。这是因为每个父类都应该被正确初始化,以确保整个继承层次结构的完整性和正确性。

8: 父类和子类类型转换需要注意什么?

答:在 C++ 中,父类和子类之间的类型转换涉及到向上转型和向下转型两个概念。这些类型转换需要注意以下几点:

向上转型(Upcasting):

  • 向上转型是指将子类的指针或引用转换为父类的指针或引用。
  • 向上转型是安全的,因为子类对象可以被看作是父类对象,而且子类继承了父类的所有成员。
  • 这种转型可以隐式进行,无需显式强制类型转换。
    #include <iostream>
    
    class Animal {
    public:
        void makeSound() {
            std::cout << "Animal makes a sound" << std::endl;
        }
    };
    
    class Dog : public Animal {
    public:
        void makeSound() {
            std::cout << "Dog barks" << std::endl;
        }
    };
    
    //创建一个 Dog 对象,并将其向上转型为 Animal 类型:
    
    int main() {
        Dog myDog;
        Animal& animalRef = myDog; // 向上转型,将 Dog 对象转换为 Animal 引用
    
        animalRef.makeSound(); // 调用的是 Dog 类中重写后的 makeSound 函数
    
        return 0;
    }
    

向下转型(Downcasting):

  • 向下转型是指将父类的指针或引用转换为子类的指针或引用
  • 向下转型是不安全的,因为父类对象并不一定是子类对象,可能会导致未定义行为。
  • 进行向下转型时,应该使用 dynamic_cast 运行时类型识别,以确保转换的安全性。
  • 向下转型可以使用dynamic_cast、static_cast 或 reinterpret_cast来实现,其中 dynamic_cast 是安全的向下转型操作符。
    #include <iostream>
    
    class Base {
    public:
        virtual void print() {
            std::cout << "Base class" << std::endl;
        }
    };
    
    class Derived : public Base {
    public:
        void print() override {
            std::cout << "Derived class" << std::endl;
        }
    
        void derivedMethod() {
            std::cout << "Derived method" << std::endl;
        }
    };
    
    int main() {
        Base* basePtr = new Derived();
        
        Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
        if (derivedPtr) {
            derivedPtr->print(); // 调用 Derived 类的成员函数
            derivedPtr->derivedMethod(); // 调用 Derived 类特有的成员函数
        } else {
            std::cout << "Failed to downcast" << std::endl;
        }
    
        delete basePtr;
        return 0;
    }
    

总的来说,在进行父类和子类之间的类型转换时,应该遵循安全性和实际需求,使用向上转型和向下转型的方式,并借助虚函数和运行时类型识别来确保转换的正确性。同时,要注意避免切片问题,以及避免在没有明确安全性保证的情况下进行向下转型。

  • 13
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值