当前位置: 首页 > article >正文

突破编程_C++_面试(基础知识(10))

面试题29:什么是嵌套类,它有什么作用

嵌套类指的是在一个类的内部定义的另一个类。嵌套类可以作为外部类的一个成员,但它与其声明类型紧密关联,不应被用作通用类型。嵌套类可以访问外部类的所有成员,包括私有成员,这是嵌套类的一个重要特性。
嵌套类的作用主要有以下几点:
(1)封装和隐藏实现细节:嵌套类可以用于隐藏实现细节,使得外部类看起来更加简洁和易于使用。
(2)提供服务:嵌套类通常是为外部类提供服务的,它可以访问外部类的所有成员,因此可以方便地实现与外部类相关的功能。
(3)代码组织:嵌套类可以用于组织代码,将相关的类放在一起,提高代码的可读性和可维护性。
需要注意的是,嵌套类不应被公开可见,除非必要。在设计良好的库中,开发人员几乎不需要使用嵌套类来实例化对象或声明变量。在大多数情况下,嵌套类应该被视为实现细节的一部分,而不是公开的 API 。
嵌套类访问权限遵循一般的类成员访问权限规则,嵌套类可以是 public 、 protected 或 private ,这取决于它们在外部类中的声明方式。这些访问修饰符决定了嵌套类在外部类的外部可见性和可访问性。
(1) public 嵌套类
如果嵌套类被声明为 public ,则它在外部类的外部是可见的,并且可以被任何能够访问外部类的代码所访问和使用。
(2) protected 嵌套类
如果嵌套类被声明为 protected ,则它在外部类的外部是不可见的,但可以被外部类的派生类访问。注意:一般很少将嵌套类定义成 protected 。
(3)Private嵌套类
如果嵌套类被声明为 private ,则它在外部类的外部是不可见的,并且只能被外部类的成员函数访问。
嵌套类在实现某些设计模式(如工厂模式、组合模式等)时也非常有用,典型的比如使用嵌套类实现工厂模式:

#include <iostream>  
#include <string>  

using namespace std;

// 外部类,作为工厂类的容器 
class AnimalFactory
{
public:
	// 嵌套类,定义不同的动物 
	class Animal
	{
	public:
		virtual void eat() {}
	};

	// 嵌套类,实现具体的动物类(猫)
	class Cat : public Animal
	{
	public:
		void eat() 
		{
			printf("cat eat\n");
		}
	};

	// 嵌套类,实现具体的动物类(狗)
	class Dog : public Animal
	{
	public:
		void eat()
		{
			printf("dog eat\n");
		}
	};

	// 工厂方法,根据传入的字符串创建相应的动物  
	static std::shared_ptr<Animal> createAnimal(const std::string& animalType) {
		if ("Cat" == animalType) 
		{
			return std::shared_ptr<Animal>(new Cat);
		}
		else if ("Dog" == animalType)
		{
			return std::shared_ptr<Animal>(new Dog);
		}
		return nullptr; // 如果类型不匹配,返回空指针  
	}
};

int main() 
{
	std::shared_ptr<AnimalFactory::Animal> cat = AnimalFactory::createAnimal("Cat");
	cat->eat();

	std::shared_ptr<AnimalFactory::Animal> dog = AnimalFactory::createAnimal("Dog");
	dog->eat();

	return 0;
}

上面代码的输出为:

cat eat
dog eat

在这个例子中,AnimalFactory 类包含了 3 嵌套类 Animal 、Cat 和 Dog,Cat 类和 Dog 类分别实现了 Animal 基类的 eat 方法。AnimalFactory 类还提供了一个静态工厂方法 createAnimal,该方法根据传入的字符串参数创建并返回相应类型的动物对象。main 函数中展示了如何使用工厂方法来创建和使用不同的对象。
使用嵌套类实现工厂模式的好处是,所有与形状相关的类都集中在 AnimalFactory 类中,这有助于保持代码的整洁和组织性。此外,通过将工厂方法放在 AnimalFactory 类中,可以确保只有这个类能够创建形状对象,这有助于控制对象的创建过程。

面试题30:什么是类静态成员,它有什么作用

类静态成员是类中声明为 static 的成员,包括静态成员变量和静态成员函数。静态数据成员是与类关联的全局变量,而不是与类的实例关联。静态成员函数可以在没有类实例的情况下调用,且只能访问静态成员变量或其他静态成员函数。
如下为类静态成员的样例:

#include <iostream> 

class MyClass
{
public:
	MyClass() {}
	~MyClass() {}

public:
	static void printStaticVal()
	{
		printf("static value = %d\n", m_staticVal);
	}

	static void setStaticVal(int val)
	{
		m_staticVal = val;
	}

private:
	static int m_staticVal;
};

int MyClass::m_staticVal = 0;

int main() 
{
	MyClass::setStaticVal(1);
	MyClass::printStaticVal();

	return 0;
}

上面代码的输出为:

static value = 1

上面代码中, MyClass 有一个静态成员变量 m_staticVal 和两个静态成员函数 printStaticVal 和 setStaticVal 。这些静态成员可以被类的任何对象访问,也可以直接通过类名访问。当通过 MyClass::setStaticVal 设置 m_staticVal 的值时,所有 m_staticVal 的对象都会看到相同的值,因为静态成员是共享的。
类静态成员有如下 5 个特点:
(1)存储:静态数据成员在内存中只有一份拷贝,无论创建多少个类的对象。
(2)初始化:静态数据成员在类外部进行初始化,且只初始化一次。
(3)访问:静态成员函数只能访问静态数据成员或其他静态成员函数,不能访问非静态成员。
(4)生命周期:静态成员的生命周期与程序的生命周期相同。
(5)共享:所有类的实例共享同一个静态数据成员。
类静态成员的主要作用是提供类级别的信息或功能,这些信息或功能与类的具体实例无关。静态数据成员可以用于存储类级别的常量、统计信息或共享资源。静态成员函数可以用于执行不依赖于类实例的操作,如修改静态数据成员或执行与类相关的全局操作。此外,静态成员函数还可以用于访问静态数据成员,并在没有类实例的情况下被调用。

面试题31:什么是浅拷贝和深拷贝,其各自适用的场景是什么

浅拷贝( Shallow Copy ):只复制对象的值或对象的引用(指针),但不复制对象引用的实际对象。如果对象内部包含动态分配的内存或其他资源,则这些资源在浅拷贝后会被多个对象共享,可能导致资源释放时的错误(如双重删除)。浅拷贝适用于对象间共享资源且不需要修改这些资源的场景。浅拷贝可以提高性能,因为不需要复制大量数据。但是,如果共享的资源被意外修改或释放,可能会导致问题。
深拷贝( Deep Copy ):复制对象的值以及对象引用的实际对象。对于包含动态分配内存的对象,深拷贝会创建这些内存区域的新副本,确保每个对象都有自己独立的资源副本。深拷贝适用于需要对象拥有独立资源副本的场景。深拷贝可以确保每个对象都可以独立修改资源而不会影响其他对象。但是,深拷贝可能占用更多的内存,并且在复制大量数据时可能会影响性能。

面试题32:C++中默认的拷贝构造函数执行的是深拷贝还是浅拷贝

C++ 中默认的拷贝构造函数执行的是浅拷贝。它只逐成员复制对象的内容,如果成员是指针或引用,那么它只是复制这些指针或引用的值,而不是它们所指向的实际对象。

面试题33:如何在C++中实现深拷贝

要实现深拷贝,需要自定义拷贝构造函数与重载赋值运算符,并在其中为动态分配的内存或其他资源创建新的副本。如下为样例代码:

#include <iostream>  

class MyClass 
{
public:
	MyClass(int value) {
		m_val = new int(value);
	}

	~MyClass() {
		delete m_val;
	}

	// 拷贝构造函数(深拷贝)  
	MyClass(const MyClass& other)
	{
		m_val = new int(*(other.m_val));
	}

	// 赋值运算符(深拷贝)  
	MyClass& operator=(const MyClass& other)
	{
		if (this != &other) 
		{
			delete m_val; // 先删除当前对象的数据  
			m_val = new int(*(other.m_val)); // 然后复制其他对象的数据  
		}
		return *this;
	}

public:
	void setVal(int val)
	{
		*m_val = val;
	}

	void printVal()
	{
		printf("val = %d\n",*m_val);
	}

private:
	int* m_val;
};

int main() {
	MyClass obj1(0);
	MyClass obj2 = obj1;
	obj2.setVal(1);	// 通过 obj2 对象修改值,obj1 不会被改变
	printf("obj1: ");
	obj1.printVal();
	printf("obj2: ");
	obj2.printVal();

	return 0;
}

上面代码的输出为:

obj1: val = 0
obj2: val = 1

在上面代码中,拷贝构造函数和赋值运算符都创建了一个新的整数,并复制了 obj1 对象中 m_val 指针指向的值。这样,每个对象都有自己独立的内存块,修改一个对象不会影响另一个对象。
深拷贝通常比浅拷贝更耗时和资源密集,因为它涉及到分配额外的内存和复制数据。然而,深拷贝提供了更好的数据完整性和安全性,特别是在涉及到多个对象共享同一份数据时。


http://www.kler.cn/a/234009.html

相关文章:

  • Flink1.19编译并Standalone模式本地运行
  • 微服务各组件整合
  • 代码修改材质参数
  • 比ChatGPT更酷的AI工具
  • 【Linux】-学习笔记03
  • 【R78/G15 开发板测评】串口打印 DHT11 温湿度传感器、DS18B20 温度传感器数据,LabVIEW 上位机绘制演化曲线
  • 学习笔记——ENM模拟
  • 微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
  • 零基础学编程从入门到精通,系统化的编程视频教程上线,中文编程开发语言工具构件之缩放控制面板构件用法
  • Centos 7系统安装proftpd-1.3.8过程
  • 图像的旋转不变特性及应用
  • React18原理: Fiber架构下的单线程CPU调度策略
  • 代码随想录-背包问题
  • vue3 之 商城项目—详情页
  • 双非本科准备秋招(21.1)—— 力扣二叉搜索树
  • C++ //练习 5.4 说明下列例子的含义,如果存在问题,试着修改它。
  • 随机MM引流源码PHP开源版
  • RabbitMQ-1.介绍与安装
  • django中实现观察者模式
  • Python面试题7-12
  • Linux无交互自动安装miniconda3
  • 004集—二调数据库标注分子分母模式及统计净面积——arcgis
  • MybatisPlus Wrapper构造器(查询篇)
  • 【Java面试】数据类型常见面试题
  • elasticsearch下载及可视化工具下载使用
  • 二十、K8S-1-权限管理RBAC详解