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

面试题24:什么是面向对象编程

面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式或编程模型,它基于对象的概念来设计和实现程序。在面向对象编程中,程序是由一系列对象组成的,这些对象是对现实世界中的实体或抽象概念的软件表示。每个对象都有其特定的属性和行为,这些属性和行为分别由对象的成员变量(或称为属性、字段等)和成员函数(或称为方法)来定义。

面向对象编程的三大基本特性是:封装( Encapsulation )、继承( Inheritance )和多态( Polymorphism )。
(1)封装(Encapsulation)

封装是指将对象的属性和方法隐藏在其内部,只通过有限的接口与外部进行交互。这样可以防止外部代码随意访问和修改对象的内部数据,从而提高代码的安全性和可维护性。
(2)继承(Inheritance)

继承是指一个类(子类)可以继承另一个类(父类)的属性和方法,从而实现代码的重用。子类可以继承父类的所有非私有属性和方法,并可以添加或覆盖父类的方法。 C++ 支持三种类型的继承:公有继承( public )、保护继承( protected )和私有继承( private )。其中,公有继承是最常用的继承方式。
(3)多态(Polymorphism)

多态是指允许一个成员函数被多种数据类型实现(重载),或者一个成员函数在不同场景下有不同实现方式(重写)。多态性允许使用基类的指针或引用来调用派生类中的成员函数,在运行时根据实际对象的类型来确定调用哪个函数,从而增强了程序的灵活性和可扩展性。

面试题25:面向对象编程的优点有哪些

(1)通过继承增强代码的可重用性:可以很方便地重用父类的属性与方法。

(2)通过封装增强代码的可维护性:可以隐藏对象的内部实现细节,只通过有限的接口与外部进行交互,从而降低代码的耦合度,提高代码的可维护性。

(3)通过多态增强代码的可扩展性:可以在不修改现有代码的情况下,增加新的功能或重构现有功能的行为。

面试题26:类的构造函数有哪些种类

根据构造函数的参数列表和特性,可以将构造函数分为 6 种不同的类型:
(1)默认构造函数

默认构造函数是一种没有参数的构造函数。如果类中没有定义任何构造函数,编译器会自动生成一个默认构造函数。如果类定义了其他构造函数,但没有定义默认构造函数,编译器就不会自动生成默认构造函数,如下为样例代码:

cpp 复制代码
class Animal1
{
};

class Animal2
{
public:
	Animal2(string name) {}
};

Animal1 animal1;		// OK:类中没有定义任何构造函数,编译器会自动生成一个默认构造函数 

Animal2 animal2;		// 错误:类定义了其他构造函数( Animal2(string name) ),但没有定义默认构造函数,编译器就不会自动生成默认构造函数 
Animal2 animal2("aa");	// OK:

(2)参数化构造函数

参数化构造函数是带有参数的构造函数。它可以有一个或多个参数,用于在创建对象时初始化对象的成员变量。如下为样例代码:

cpp 复制代码
class Animal
{
public:
	Animal(string name) {}
};

Animal animal("aa");	// OK:

(3)拷贝构造函数

拷贝构造函数是一种特殊的构造函数,它使用一个已存在的对象来初始化新创建的对象。它的参数是对同类型对象的常量引用。如果类没有显式定义拷贝构造函数,编译器会提供一个默认的拷贝构造函数。如下为样例代码:

cpp 复制代码
#include <iostream>  
#include <string>  

using namespace std;

class Animal
{
public:
	Animal() 
	{
		printf("call Animal()\n");
	}
	Animal(const Animal& animal)
	{
		printf("call Animal(const Animal& animal)\n");
	}
};

int main() {
	Animal animal1;				// 调用无参数的构造函数
	Animal animal2(animal1);	// 调用拷贝构造函数
	return 0;
}

上面代码的输出为:

call Animal()
call Animal(const Animal& animal)

(4)列表初始化构造函数

列表初始化构造函数使用成员初始化列表来初始化对象的数据成员,C++11 的列表初始化特性需要该种类型构造函数的支持。如下为样例代码:

cpp 复制代码
#include <iostream>  
#include <string>  

using namespace std;

class Animal
{
public:
	Animal(const string& name) : m_name(name)
	{
		printf("call Animal(const string& name) : m_name(name)\n");
	}

private:
	string m_name;
};

int main() {		
	Animal animal{"aa"};	// 调用列表初始化构造函数
	return 0;
}

上面代码的输出为:

call Animal(const string& name) : m_name(name)

(5)委托构造函数

委托构造函数是一种特殊的构造函数,它调用同类的另一个构造函数来执行初始化。这可以通过使用类名并跟随参数列表来实现。C++11 及以后的版本支持委托构造函数。如下为样例代码:

cpp 复制代码
#include <iostream>  
#include <string>  

using namespace std;

class Animal
{
public:
	Animal() : Animal("default")
	{
		printf("Animal() : Animal(\"default\")\n");
	}
	Animal(const string& name) : m_name(name)
	{
		printf("call Animal(const string& name) : m_name(name)\n");
	}

private:
	string m_name;
};

int main() {		
	Animal animal;
	return 0;
}

上面代码的输出为:

call Animal(const string& name) : m_name(name)
Animal() : Animal("default")

注意:先调用的是委托构造函数。
(6)移动构造函数

移动构造函数是一种特殊的构造函数,它使用右值引用参数将资源从一个对象移动到另一个对象,而不是复制。这通常用于优化性能,特别是在处理如 vector 、 string 等可能包含动态分配资源的类型时。如下为样例代码:

cpp 复制代码
#include <iostream>  
#include <string>  

using namespace std;

class Animal
{
public:
	Animal()
	{
		printf("call Animal()\n");
	}
	Animal(const Animal& animal)
	{
		printf("call Animal(const Animal& animal)\n");
	}
	Animal(Animal&& other) noexcept		//移动构造函数
	{
		printf("call Animal(Animal&& other)\n");
	}
};

int main() {		
	Animal animal1;					// 调用无参数构造函数
	Animal animal2 = animal1;		// 调用拷贝构造函数
	Animal animal3 = move(animal1); // 调用移动构造函数
	return 0;
}

上面代码的输出为:

call Animal()
call Animal(const Animal& animal)
call Animal(Animal&& other)

面试题27:类的访问修饰符权限如何划分

本类 继承类 其他
private × ×
protected ×
public

其中值得注意的是 protected 访问修饰符的用法:该访问修饰符在设计继承层次结构时特别有用。它允许派生类访问基类的实现细节,同时仍然保持对这些细节的封装和隐藏。这有助于实现更灵活和可维护的代码结构。
注意 protected 和 private 的区别:

如果其他类从该类派生(无论是公开派生还是私有派生),那么派生类中的成员函数可以访问基类中的 protected 成员。这是 protected 与 private 的区别,因为 private 成员在派生类中是不可访问的。

面试题28:this指针在C++中是如何工作的

this 是一个特殊的指针,它代表对象自身。this 指针是隐式传递给每个成员函数的,它允许成员函数访问和修改调用它的对象的成员。this 指针在成员函数内部是自动可用的,不需要显式声明。它通常用于区分成员变量和参数名称相同的情况,或者在函数内部引用当前对象的其他成员:

cpp 复制代码
class Animal
{
public:
	Animal(string name)
	{
		//name = name;		// 如果这里不使用 this ,则无法分别这个变量 name 是入参还是成员变量
		this->name = name;
	}

public:
	string getName()
	{
		return this->name;	// 此处的 this 可以用也可以不用
	}

private:
	string name;
};

在上面的代码中,构造函数 Animal(string name) 和成员函数 getName() 都使用了 this 指针。在构造函数 Animal(string name) 中,this->name表示对象的 name 成员变量,而构造函数的入参也是 name 。使用了 this 指针可以使得代码更加清晰,尤其是在成员变量和参数名称相同或相似的情况下。

需要注意的是,在大多数情况下,this 指针的使用是隐式的,编译器会自动处理。只有在需要区分成员变量和函数参数,或者需要在成员函数中显式引用当前对象时,才需要显式地使用 this 指针。

相关推荐
霁月风1 小时前
设计模式——适配器模式
c++·适配器模式
jrrz08281 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
咖啡里的茶i2 小时前
Vehicle友元Date多态Sedan和Truck
c++
海绵波波1072 小时前
Webserver(4.9)本地套接字的通信
c++
@小博的博客2 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
爱吃喵的鲤鱼3 小时前
linux进程的状态之环境变量
linux·运维·服务器·开发语言·c++
7年老菜鸡3 小时前
策略模式(C++)三分钟读懂
c++·qt·策略模式
Ni-Guvara4 小时前
函数对象笔记
c++·算法
测试19984 小时前
2024软件测试面试热点问题
自动化测试·软件测试·python·测试工具·面试·职场和发展·压力测试
似霰4 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder