前言
初学 C++ 面向对象时,很多人会被这几个概念绕晕:
为什么要写两层 namespace?虚函数到底解决什么问题?虚函数和抽象类、纯虚函数什么关系?protected 权限怎么用?为什么工程里虚函数常放 protected?和 Java 的接口、抽象类又有什么区别?
今天用大白话 + 实战规范 + Java 横向对比,一次性全部梳理清楚,看完直接能用于项目开发和面试。
一、为什么 C++ 要写两层 namespace?
1. 什么是两层 namespace
示例代码:
cpp
namespace myproject {
namespace core {
// 核心模块代码、函数、类
}
}
2. 核心作用
两层命名空间逻辑:外层圈项目/公司,内层圈功能模块。
- 第一层
myproject:相当于品牌/项目名,全局唯一,防止和第三方库命名冲突; - 第二层
core:相当于功能分类,专门存放核心逻辑、公共组件、基础模块。
3. 类比其他语言
- C 语言 :没有 namespace,只能给函数/变量手动加前缀模拟,比如
myproject_core_func(); - Python :namespace 等价于包/模块/文件夹,通过 import 导入隔离命名;
4. 为什么不推荐只写一层
只写 namespace core,别人的项目也可能定义同名空间,极易命名冲突 ;
两层嵌套 myproject::core 全局唯一,大型项目、开源框架全是这种写法。
5. 大厂开源项目通用写法
Qt、Google Abseil、虚幻引擎,全是外层项目名、内层功能模块的多层 namespace 套路,结构清晰、隔离彻底。
二、C++ 虚函数到底是什么?为什么要设计虚函数?
1. 大白话定义
虚函数:父类定义函数,允许子类重写;用父类指针指向子类对象时,自动调用子类的实现,实现多态。
2. 无虚函数 vs 有虚函数 对比
没有虚函数:父类指针调用父类方法。
有虚函数:父类指针自动调用子类方法。
3. 为什么语言要设计虚函数?
- 实现多态:一个统一接口,不同子类有不同实现;
- 开闭原则:父类定框架,新增功能只加子类,不用修改旧代码;
- 统一管理:可以用父类容器存放所有子类对象,循环统一调用方法;
- 消灭臃肿 if-else:不用大量判断类型来调用对应函数。
三、override 关键字:可以不写,但强烈建议写
1. 能不能不写?
能。子类重写虚函数,不写 override 也能正常多态,编译运行都没问题。
2. 为什么推荐必须写
override 不是给程序用的,是给编译器做语法校验:
- 防止函数名手写错;
- 防止参数列表、返回值和父类对不上;
- 防止不小心写成新函数,而非重写;
一旦签名不匹配,编译器直接报错,提前规避隐性 Bug,团队开发规范强制要求加。
四、虚函数、纯虚函数、抽象类、抽象函数关系
1. 概念对齐
- 普通虚函数 :
virtual void func(){},父类有默认实现,子类可重写、也可不重写; - 纯虚函数(抽象函数) :
virtual void func() = 0;,父类不实现,强制子类必须重写; - 抽象类 :包含至少一个纯虚函数的类,不能 new 实例化,只能作为父类被继承。
2. 核心区别
- 普通虚函数类:可以创建对象,子类可选重写;
- 抽象类:不能创建对象,子类必须实现所有纯虚函数才能实例化。
3. 和 Java 对应关系
- C++ 纯虚函数 ⇋ Java 抽象方法;
- C++ 含纯虚函数的抽象类 ⇋ Java 抽象类;
- C++ 没有
interface关键字,用全纯虚函数的类模拟接口。
五、C++ 与 Java 编程习惯差异
- C++
- 默认成员函数不是虚函数,必须手动加
virtual; - 做接口规范:用纯虚函数抽象类;
- 做多态覆写:用普通虚函数。
- Java
- 普通方法默认自带动态绑定,天生等价 C++ 虚函数;
- 做规范、强制实现:直接用
interface接口 +abstract抽象类; - 架构常用:抽象类做模板、接口做能力定义。
总结一句:C++ 靠纯虚函数模拟接口,Java 原生用 interface 做接口;本质都是父类定规范,子类实现细节。
六、protected 权限精讲(public/private/protected 对比)
1. 三大权限大白话
public:所有人都能访问(外部、子类、本类);private:只有本类自己能用,子类都无权访问;protected:本类能用 + 子类能用 + 外部其他人都不能用。
2. 生活化类比
- public:小区广场,谁都能进;
- private:个人卧室,只自己用;
- protected:家庭家产,自己能用、子女能继承,外人无权动用。
七、工程规范:父类虚函数为什么常用 protected?
1. 核心规范
- 需要对外直接给外部调用 的虚函数:写
public; - 只给子类重写、不对外暴露 的虚函数:写
protected(项目 80% 都是这种)。
2. 工业级标准模板写法(模板方法模式)
cpp
class Base
{
public:
// 对外公开固定接口
void exec()
{
preWork();
doExec();
afterWork();
}
// 虚析构,继承必备
virtual ~Base() = default;
private:
void preWork() { }
void afterWork(){ }
protected:
// 纯虚函数:留给子类重写
virtual void doExec() = 0;
};
3. 这种写法的好处
- 固定外层业务流程,只把变化点留给子类扩展;
- 隐藏内部虚函数,外部不能随意调用,封装性更好;
- 遵循设计模式模板方法模式,大厂 C++ 框架标准写法;
- 权限隔离清晰:public 对外、protected 给子类、private 内部私有。
八、全文核心总结
- 两层 namespace:外层项目隔离,内层功能分类,避免命名冲突;
- 虚函数核心:实现多态,父类定框架,子类做扩展,消灭大量 if-else;
- override 建议必加:编译期校验函数签名,避免隐性重写失败 Bug;
- 纯虚函数=抽象函数,含纯虚函数的类=抽象类,不能实例化,强制子类实现;
- C++ 无 interface,用纯虚类模拟接口;Java 原生用 interface,思想完全一致;
- protected:本类+子类可用,外部不可用,专门给子类继承复用;
- 工程规范:对外接口 public,留给子类重写的虚函数统一放 protected,标准框架写法。