C++ 抽象

一,纯虚函数:没有函数体的虚函数

abstract_base.h

/**
 * C++中的纯虚函数(或抽象函数)是我们没有实现的虚函数!我们只需声明它!通过声明中赋值0来声明纯虚函数!
 * 纯虚函数:没有函数体的虚函数
*/

/**
 * 抽象类
*/
class AbstractBase {
    public:
    // 纯虚函数
    virtual void show() = 0;
}

abstract.cpp

#include <iostream>
using namespace std;

class A {
    public:
    virtual void f() = 0; //纯虚函数
    void g() { this-> f(); }
    A() {}
};

class B: public A {
    public:
    // 实现父类A中的虚函数
    void f() {
        cout << "B:f()" << endl;
    }
};

int main() {
    B b;
    b.g();
    return 0;
}

编译运行

二,抽象类由派生类集成实现!

derived_full.cpp

/**
 * 抽象类由派生类集成实现!
*/

#include <iostream>
using namespace std;

class Base {
    int x;

    public:
        virtual void fun() = 0;
        int getX() {return x;}
};

class Derived : public Base {
    public:
        void fun() {cout << "fun() called" << endl; } //实现fun()函数
};

int main(void) {
    Derived d;
    d.fun(); // 不是new出来的,用.
    return 0;
}

三,抽象类可以有构造函数

interesting_facts1.cpp

/**
 * 纯虚函数使一个类变成抽象类
*/

#include <iostream>
using namespace std;

/**
 * 抽象类至少包含一个纯虚函数
*/
class Test {
    int x;

public:
    virtual void show() = 0;
    int getX() {return x;}
};

int main(void) {
    Test t; // error!不能创建抽象类的对象
    return 0;
}

四, 抽象类类型的指针和引用

interesting_facts2.cpp

/**
 * 抽象类类型的指针和引用
*/

#include <iostream>
using namespace std;

/**
 * 抽象类至少包含一个纯虚函数
*/
class Base {
    int x;

    public:
        virtual void show() = 0;
        int getX() { return x; }
};

class Derived : public Base {
    public:
    void show() { cout << "In derived \n"; }
    Derived() {}
};

int main(void) {
    // Base b; // error! 不能创建抽象类的对象
    // Base *b = new Base(); error!
    Base *bp = new Derived(); //抽象类的指针与引用 -> 由抽象类派生出来的类的对象
    bp->show();
    return 0;
}

五,如果我们不在派生类中覆盖纯虚函数,那么派生类也会变成抽象类

interesting_facts3.cpp

/**
 * 如果我们不在派生类中覆盖纯虚函数,那么派生类也会变成抽象类
*/

#include <iostream>
using namespace std;

class Base {
    int x;

    public:
        virtual void show() = 0;
        int getX() { return x; }
};

/**
 * Derived 自动变为抽象类
 */
class Derived : public Base {
    public:
    // void show() {}
};
int main(void) {
    Derived
    d; // error! 派生类没有实现纯虚函数,那么派生类也会变成抽象类,不能创建抽象类的对象
    return 0;
}

六,抽象类可以有构造函数

interesting_facts4.cpp

/**
 * 抽象类可以有构造函数
*/

#include <iostream>
using namespace std;

// An abstract class with constructor
class Base {
    protected:
    int x;
    
    public:
        virtual void fun() = 0;
        // 抽象类的构造函数
        Base(int i) { x = i; }
};

class Derived : public Base {
    int y;

    public:
    // 调用父类构造函数
    Derived(int i, int j) : Base(i) { y = j; }
    // 实现父类抽象函数
    void fun() { cout << "x = " << x << ", y = " << y; }
};

int main(void) {
    Derived d(4, 5);
    d.fun();
    return 0;
}

七,构造函数不能是虚函数,而析构函数可以是虚析构函数。

interesting_facts5.cpp

/**
 * 构造函数不能是虚函数,而析构函数可以是虚析构函数。
 * 例如:当基类指针指向派生类对象并删除对象时,我们可能希望
 * 调用适当的析构函数。如果析构函数不是虚拟的,则只能调用基类的析构构函数。
*/

#include <iostream>
using namespace std;

class Base {
    public:
    Base() { cout << "Constructor: Base" << endl; }
    virtual ~Base() { cout << "Destructor: Base" << endl; }
};

class Derived : public Base {
    public:
        Derived() { cout << "Constructor: Derived" << endl; }
        ~Derived() { cout << "Destructor: Derived" << endl; }
};

int main() {
    Base *Var = new Derived();
    delete Var;
    return 0;
}

八, 纯虚函数:没有函数体的虚函数。抽象类:包含纯虚函数的类

pure_virtual.cpp

/**
 * 纯虚函数:没有函数体的虚函数
 * 抽象类:包含纯虚函数的类
*/

#include <iostream>

using namespace std;

class A {
    private:
        int a;

    public:
        virtual void show() = 0; // 纯虚函数
};

int main() {

    /**
     * 1. 抽象类只能作为基类来派生新类使用
     * 2. 抽象类的指针和引用->由抽象类派生出来的类的对象!
    */

   A a; // error 抽象类,不能创建对象
   A *a1; // ok 可以定义抽象类的指针
   A *a2 = new A(); // error,A是抽象类,不能创建对象
}

### C++抽象语法树 (Abstract Syntax Tree, AST) 的构建与应用 #### 什么是抽象语法树? 抽象语法树是一种用于表示程序源代码的中间形式的数据结构。它是编译器前端的重要产物之一,能够捕捉源代码的主要语法规则并忽略无关紧要的细节[^1]。 #### 抽象语法树的作用 在编译过程中,抽象语法树充当了一个桥梁的角色,连接了词法分析和语义分析两个阶段。通过构建抽象语法树,可以简化复杂的源代码逻辑,并将其转化为易于操作的树形数据结构。这种转化对于优化、代码生成以及静态分析等后续工作至关重要[^2]。 #### 如何使用 C++ 实现抽象语法树? 以下是基于 C++ 构建抽象语法树的一些核心概念和技术要点: 1. **词法分析** 首先需要将输入的源代码分解为一系列标记(Token)。这些标记代表基本的语言单元,比如关键字、运算符、变量名等。这一过程通常由一个称为扫描器(Lexer)或分词器(Tokenizer)的模块完成。 2. **语法分析** 接下来利用 LL 或 LR 解析算法来验证标记序列是否符合目标语言的语法规则。在此基础上逐步构造出对应的抽象语法树节点。递归下降解析方法因其直观性和易实现性,在许多实际项目中得到了广泛应用。 3. **AST 节点的设计** 每种类型的表达式或者声明都需要对应一种特定的 AST 节点类。例如,“赋值表达式”可能有一个 `AssignmentNode` 类型;而函数调用可能会有另一个专门的节点类型如 `FunctionCallNode` 。所有这些具体子类都应该继承自一个通用基类 `AstNode` ,以便统一管理和遍历整个树结构[^4]。 ```cpp class AstNode { public: virtual ~AstNode() {} }; class AssignmentNode : public AstNode { private: std::string variableName; AstNode* value; public: AssignmentNode(const std::string& name, AstNode* val) : variableName(name), value(val) {} void accept(AstVisitor& visitor); }; ``` 4. **符号表管理** 符号表用来记录所有的标识符及其属性信息(类型、作用域范围等),这对于正确解释 AST 至关重要。每当遇到新的定义时就需更新该表格;而在查找某个名字的意义之前也要查询此表以确认其存在与否及确切含义。 5. **错误处理机制** 编写良好的语法分析器还应该具备完善的异常捕获能力,当发现非法句型时能及时报告给用户具体的错位位置及相关提示消息。 6. **现有工具的应用** 如果不想完全从零开始开发自己的 AST 系统,则可考虑借助现有的开源解决方案,像 cppast 就是一个不错的选择。它不仅支持多种版本的标准 C++ 特征描述,而且允许使用者轻松提取所需的信息片段来进行进一步加工处理[^3]。 --- ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值