为了账号安全,请及时绑定邮箱和手机立即绑定

C++高级语法入门指南

标签:
C++
概述

本文深入介绍了C++高级语法,涵盖类和对象、模板和泛型编程、异常处理机制、常用高级特性、内存管理和面向对象高级设计模式。通过详细的示例代码,帮助读者理解并掌握C++高级语法的入门知识。

深入理解类和对象

在C++编程中,类和对象的概念是面向对象编程的基础。类是一种用户自定义的数据类型,它封装了相关的数据(成员变量)和方法(成员函数)。对象则是类的一个实例。本节将深入探讨类和对象的关键概念,包括访问控制符、构造函数和析构函数,以及静态成员变量和成员函数。

访问控制符(public, protected, private)

访问控制符用于控制成员变量和成员函数的访问级别。C++提供了三种访问控制符:public、protected 和 private。不同的访问控制符决定了类的成员可以被哪些部分访问。

  • public(公共):可以被任何地方访问。
  • protected(保护):仅可以在类本身和派生类中访问。
  • private(私有):只能在类本身内访问。

下面是一个示例,展示了如何使用这些访问控制符:

#include <iostream>

class ExampleClass {
public:
    int publicVar;  // 公共成员变量,任何位置都可以被访问
    void publicMethod() {
        std::cout << "Public method called." << std::endl;
    }

protected:
    int protectedVar;  // 保护成员变量,仅类本身和派生类可以访问
    void protectedMethod() {
        std::cout << "Protected method called." << std::endl;
    }

private:
    int privateVar;  // 私有成员变量,仅类本身可以访问
    void privateMethod() {
        std::cout << "Private method called." << std::endl;
    }
};

int main() {
    ExampleClass obj;

    // 访问公共成员
    obj.publicVar = 10;
    obj.publicMethod();

    // 访问保护成员(通过派生类)
    class DerivedClass : public ExampleClass {
    public:
        void accessProtectedMethod() {
            protectedVar = 20;
            protectedMethod();
        }
    };

    DerivedClass derivedObj;
    derivedObj.accessProtectedMethod();

    // 尝试访问私有成员将导致编译错误
    // obj.privateVar = 30;  // 编译错误
    // obj.privateMethod();  // 编译错误

    return 0;
}

构造函数和析构函数

构造函数和析构函数是特殊的成员函数,用于初始化和清理对象。构造函数用于初始化对象,而析构函数用于清理对象。

构造函数

  • 构造函数的名称与类名相同。
  • 构造函数没有返回类型,即使是void。
  • 构造函数可以有参数,用于初始化对象。
  • 构造函数可以重载,允许使用不同的参数列表。

析构函数

  • 析构函数名称以 ~ 开头,后跟类名。
  • 析构函数没有返回类型,不能返回任何值。
  • 析构函数不能有参数。
  • 析构函数用于清理对象资源,例如释放内存。

下面是一个示例,展示了如何使用构造函数和析构函数:

#include <iostream>

class ExampleClass {
public:
    int value;

    // 构造函数
    ExampleClass() {
        std::cout << "Default constructor called." << std::endl;
        value = 0;
    }

    ExampleClass(int val) {
        std::cout << "Parameterized constructor called." << std::endl;
        value = val;
    }

    // 析构函数
    ~ExampleClass() {
        std::cout << "Destructor called." << std::endl;
    }
};

int main() {
    ExampleClass obj1;  // 调用默认构造函数
    ExampleClass obj2(10);  // 调用参数化构造函数

    std::cout << "Value of obj1: " << obj1.value << std::endl;
    std::cout << "Value of obj2: " << obj2.value << std::endl;

    // 添加一个重载的构造函数示例
    ExampleClass obj3(20);  // 调用参数化构造函数
    std::cout << "Value of obj3: " << obj3.value << std::endl;

    return 0;
}

静态成员变量和成员函数

静态成员变量和成员函数是类的成员,但它们属于类而不是任何特定的对象。静态成员可以被所有对象共享。

静态成员变量

  • 静态成员变量在所有对象中共享。
  • 静态成员变量必须在类外部进行初始化。
  • 静态成员变量可以在类内声明,在类外初始化。

静态成员函数

  • 静态成员函数可以访问静态成员变量。
  • 静态成员函数不能访问非静态成员变量或成员函数。
  • 静态成员函数可以通过类名直接调用,不需要对象实例。

下面是一个示例,展示了如何使用静态成员变量和成员函数:

#include <iostream>

class ExampleClass {
public:
    static int staticVar;  // 声明静态成员变量
    static void staticMethod() {
        std::cout << "Static method called." << std::endl;
        std::cout << "Static variable: " << staticVar << std::endl;
    }
};

// 初始化静态成员变量
int ExampleClass::staticVar = 10;

int main() {
    // 调用静态成员函数
    ExampleClass::staticMethod();

    ExampleClass obj1;
    ExampleClass::staticVar = 20;  // 修改静态成员变量
    ExampleClass::staticMethod();

    return 0;
}

通过以上内容,我们了解了类和对象中的访问控制符、构造函数和析构函数、静态成员变量和成员函数。这些概念是C++面向对象编程的基础,对于深入理解类和对象非常重要。

模板和泛型编程基础

模板是C++中一种重要的特性,它允许代码的通用性和重用性。模板分为函数模板和类模板,分别用于生成通用的函数和类。本节将介绍C++中的模板和泛型编程基础,包括函数模板、类模板以及标准模板库(STL)的简介。

函数模板

函数模板是一种通用的函数定义,可以在编译时根据具体类型生成特定的函数。函数模板允许编写可以处理多种类型的通用代码。

函数模板的声明
函数模板的声明以关键字 template 开头,后跟一个或多个类型参数,类型参数通常用 typenameclass 关键字声明。

函数模板的使用
使用函数模板时,编译器会根据实际类型参数生成特定的函数。

下面是一个示例,展示了如何使用函数模板:

#include <iostream>

// 函数模板声明
template <typename T>
T add(T a, T b) {
    return a + b;
}

int main() {
    // 使用函数模板进行整数加法
    int intResult = add<int>(5, 10);
    std::cout << "Integer result: " << intResult << std::endl;

    // 使用函数模板进行浮点数加法
    float floatResult = add<float>(3.5, 4.7);
    std::cout << "Float result: " << floatResult << std::endl;

    // 使用函数模板进行字符串拼接
    std::string strResult = add<std::string>("Hello, ", "World!");
    std::cout << "String result: " << strResult << std::endl;

    return 0;
}

类模板

类模板是一种通用的类模板,可以在编译时根据具体类型生成特定的类。类模板允许编写可以处理多种类型的通用类。

类模板的声明
类模板的声明以关键字 template 开头,后跟一个或多个类型参数,类型参数通常用 typenameclass 关键字声明。

类模板的使用
使用类模板时,编译器会根据实际类型参数生成特定的类。

下面是一个示例,展示了如何使用类模板:

#include <iostream>

// 类模板声明
template <typename T>
class Stack {
private:
    T* elements;
    int top;
    int capacity;

public:
    Stack(int initialCapacity) {
        capacity = initialCapacity;
        elements = new T[capacity];
        top = -1;
    }

    ~Stack() {
        delete[] elements;
    }

    void push(T value) {
        if (top >= capacity) {
            // 扩容
            T* newElements = new T[capacity * 2];
            for (int i = 0; i < capacity; i++) {
                newElements[i] = elements[i];
            }
            delete[] elements;
            elements = newElements;
            capacity *= 2;
        }
        elements[++top] = value;
    }

    T pop() {
        if (top == -1) {
            throw "Stack is empty";
        }
        return elements[top--];
    }

    T peek() {
        if (top == -1) {
            throw "Stack is empty";
        }
        return elements[top];
    }

    bool isEmpty() {
        return top == -1;
    }
};

int main() {
    // 使用类模板创建一个整数栈
    Stack<int> intStack(3);
    intStack.push(1);
    intStack.push(2);
    std::cout << "Top element of intStack: " << intStack.peek() << std::endl;

    // 使用类模板创建一个字符串栈
    Stack<std::string> stringStack(2);
    stringStack.push("Hello");
    stringStack.push("World");
    std::cout << "Top element of stringStack: " << stringStack.peek() << std::endl;

    return 0;
}

标准模板库(STL)简介

标准模板库(STL)是C++的一个重要组成部分,它提供了一系列的容器、算法和迭代器,使得编写通用代码变得更加容易。STL提供了丰富的容器类型,如vector、list、map等,还提供了一套算法,如sort、find、transform等。

常见的容器类型

  • std::vector:动态数组,支持随机访问。
  • std::list:双向链表,支持高效插入和删除。
  • std::map:关联容器,基于红黑树,支持键值对存储。

常见的算法

  • std::sort:排序算法。
  • std::find:查找算法。
  • std::transform:转换算法。

下面是一个示例,展示了如何使用STL中的vector和sort算法:

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    // 使用vector容器
    std::vector<int> numbers = {5, 2, 8, 1, 9};

    // 使用sort算法进行排序
    std::sort(numbers.begin(), numbers.end());

    // 输出排序后的结果
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

通过以上内容,我们了解了C++中的模板和泛型编程基础,包括函数模板、类模板以及标准模板库(STL)的简介。模板和STL的使用可以极大地提高代码的通用性和重用性。

异常处理机制

在C++编程中,异常处理机制是处理程序错误和异常情况的重要工具。通过抛出和捕获异常,可以有效地处理运行时错误,提高程序的健壮性和稳定性。本节将介绍如何使用异常处理机制,包括抛出异常、捕获异常以及自定义异常类。

抛出异常

在C++中,异常是通过 throw 语句抛出的。throw 语句可以抛出任何类型的对象,包括内置类型和自定义类型。

抛出异常的基本语法

throw expression;

expression 可以是任何类型的表达式,通常是表示错误信息的对象。下面是一个示例,展示了如何抛出异常:

#include <iostream>
#include <exception>

void divide(int numerator, int denominator) {
    if (denominator == 0) {
        throw std::runtime_error("Divide by zero error");
    }
    std::cout << "Result: " << numerator / denominator << std::endl;
}

int main() {
    try {
        divide(10, 0);  // 尝试除以零
    } catch (const std::exception& e) {
        std::cerr << "Exception caught: " << e.what() << std::endl;
    }
    return 0;
}

捕获异常

捕获异常使用 trycatch 块。try 块包含可能抛出异常的代码,catch 块处理抛出的异常。

捕获异常的基本语法

try {
    // 可能抛出异常的代码
} catch (exception_type_1& e1) {
    // 处理特定类型的异常
} catch (exception_type_2& e2) {
    // 处理另一个特定类型的异常
} catch (...) {
    // 处理所有未指定类型的异常
}

下面是一个示例,展示了如何捕获不同类型的异常:

#include <iostream>
#include <exception>

void throwException(int type) {
    try {
        if (type == 1) {
            throw std::runtime_error("Runtime error");
        } else if (type == 2) {
            throw std::logic_error("Logic error");
        } else {
            throw "Unknown error";
        }
    } catch (const std::runtime_error& re) {
        std::cerr << "Runtime error caught: " << re.what() << std::endl;
    } catch (const std::logic_error& le) {
        std::cerr << "Logic error caught: " << le.what() << std::endl;
    } catch (const char* msg) {
        std::cerr << "Unknown error caught: " << msg << std::endl;
    }
}

int main() {
    throwException(1);  // 抛出 runtime_error
    throwException(2);  // 抛出 logic_error
    throwException(3);  // 抛出未知错误

    return 0;
}

自定义异常类

自定义异常类允许创建具有特定行为和信息的自定义异常类。自定义异常类通常继承自标准异常类,如 std::exception,并重载 what() 方法,以提供自定义的错误信息。

自定义异常类的基本语法

class CustomException : public std::exception {
public:
    CustomException(const char* message) : msg(message) {}
    const char* what() const throw() {
        return msg;
    }
private:
    const char* msg;
};

下面是一个示例,展示了如何创建和使用自定义异常类:

#include <iostream>
#include <exception>

class CustomException : public std::exception {
public:
    CustomException(const char* message) : msg(message) {}
    const char* what() const throw() {
        return msg;
    }
private:
    const char* msg;
};

void throwCustomException() {
    throw CustomException("Custom exception message");
}

int main() {
    try {
        throwCustomException();
    } catch (const CustomException& e) {
        std::cerr << "Custom exception caught: " << e.what() << std::endl;
    }
    return 0;
}

通过以上内容,我们了解了C++中的异常处理机制,包括如何抛出异常、捕获异常以及如何自定义异常类。良好的异常处理机制可以提高程序的健壮性和可靠性。

常用高级特性

在C++编程中,除了基础的语法和特性外,还有一些高级特性可以帮助提高代码的可维护性和效率。这些高级特性包括命名空间、智能指针和迭代器与容器。本节将详细介绍这些高级特性,并通过示例代码来展示它们的使用方法。

命名空间

命名空间是C++中用于组织和管理代码的一种机制。它允许将一组相关的函数、变量和类型组织在一个单独的命名空间中,以避免命名冲突。

命名空间的基本语法

namespace namespace_name {
    // 定义变量、函数、类型等
}

下面是一个示例,展示了如何使用命名空间:

#include <iostream>

namespace myNamespace {
    int value = 10;

    void printValue() {
        std::cout << "Value in namespace: " << value << std::endl;
    }
}

int main() {
    using namespace myNamespace;  // 使用命名空间
    printValue();

    // 直接访问命名空间内的变量
    std::cout << "Direct access to namespace variable: " << myNamespace::value << std::endl;

    return 0;
}

智能指针

智能指针是C++中用于管理动态内存的一种机制。它们提供了比原始指针更安全和方便的内存管理方式。C++标准库提供了几种智能指针,包括 std::unique_ptrstd::shared_ptr

std::unique_ptr
std::unique_ptr 是一种独占所有权的智能指针,它确保每个资源只能由一个指针管理。当 std::unique_ptr 被销毁时,它会自动释放所管理的资源。

std::shared_ptr
std::shared_ptr 是一种共享所有权的智能指针,允许多个指针共享同一个资源。当最后一个 std::shared_ptr 被销毁时,它会释放所管理的资源。

下面是一个示例,展示了如何使用 std::unique_ptrstd::shared_ptr

#include <iostream>
#include <memory>

void use_unique_ptr() {
    std::unique_ptr<int> ptr1(new int(10));
    std::cout << "Value in unique_ptr: " << *ptr1 << std::endl;

    // 智能指针会自动释放资源
}

void use_shared_ptr() {
    std::shared_ptr<int> ptr1(new int(20));
    std::shared_ptr<int> ptr2 = ptr1;

    std::cout << "Value in shared_ptr 1: " << *ptr1 << std::endl;
    std::cout << "Value in shared_ptr 2: " << *ptr2 << std::endl;

    // 智能指针会自动释放资源
}

int main() {
    use_unique_ptr();
    use_shared_ptr();

    return 0;
}

迭代器和容器

迭代器是C++中用于遍历容器中元素的一种机制。容器是一系列能够存储和管理一组对象的数据结构。标准模板库(STL)提供了多种容器类型和迭代器类型。

常见的容器类型

  • std::vector:动态数组。
  • std::list:双向链表。
  • std::map:关联容器,基于红黑树。

迭代器的使用方法
迭代器通常通过容器的成员函数 begin()end() 获取。begin() 返回第一个元素的迭代器,end() 返回容器末尾之后的位置,用于遍历容器。

下面是一个示例,展示了如何使用迭代器和容器:

#include <iostream>
#include <vector>
#include <list>

void use_vector() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << "Value in vector: " << *it << std::endl;
    }
}

void use_list() {
    std::list<int> lst = {10, 20, 30, 40, 50};

    for (auto it = lst.begin(); it != lst.end(); ++it) {
        std::cout << "Value in list: " << *it << std::endl;
    }
}

int main() {
    use_vector();
    use_list();

    return 0;
}

通过以上内容,我们了解了C++中的命名空间、智能指针和迭代器与容器。这些高级特性可以提高代码的可维护性、安全性和效率。

内存管理

在C++编程中,内存管理是一个关键主题。正确的内存管理可以提高程序的性能和稳定性,避免内存泄漏和其他内存相关的问题。本节将详细介绍C++中的内存管理技术,包括动态内存分配与释放、newdelete 运算符,以及内存泄漏检测方法。

动态内存分配与释放

动态内存分配使得程序可以在运行时根据需要分配和释放内存。C++提供了 newdelete 运算符来管理动态内存。

动态内存分配
使用 new 运算符分配内存。new 会返回一个指向新分配内存的指针。

int* ptr = new int;  // 分配一个整数类型的内存

动态内存释放
使用 delete 运算符释放由 new 分配的内存。

delete ptr;  // 释放由 ptr 指向的内存

下面是一个示例,展示了如何进行动态内存分配和释放:

#include <iostream>

int main() {
    int* ptr = new int;  // 分配一个整数类型的内存
    *ptr = 10;

    std::cout << "Value in dynamically allocated memory: " << *ptr << std::endl;

    delete ptr;  // 释放由 ptr 指向的内存
    ptr = nullptr;  // 将指针置为空,避免悬挂指针

    return 0;
}

newdelete 运算符

newdelete 运算符是C++中用于动态内存分配和释放的主要工具。它们可以用于单个对象以及数组的分配和释放。

new 运算符

  • new 用于分配单个对象的内存。
  • new[] 用于分配数组的内存。
int* singlePtr = new int;  // 分配一个整数类型的内存
int* arrayPtr = new int[5];  // 分配一个整数数组的内存

delete 运算符

  • delete 用于释放分配给单个对象的内存。
  • delete[] 用于释放分配给数组的内存。
delete singlePtr;  // 释放单个对象的内存
delete[] arrayPtr;  // 释放数组的内存

下面是一个示例,展示了如何使用 newdelete 运算符:

#include <iostream>

int main() {
    int* singlePtr = new int;  // 分配一个整数类型的内存
    *singlePtr = 10;

    int* arrayPtr = new int[5];  // 分配一个整数数组的内存
    for (int i = 0; i < 5; i++) {
        arrayPtr[i] = i * 10;
    }

    std::cout << "Value in singlePtr: " << *singlePtr << std::endl;
    std::cout << "Values in arrayPtr: ";
    for (int i = 0; i < 5; i++) {
        std::cout << arrayPtr[i] << " ";
    }
    std::cout << std::endl;

    delete singlePtr;  // 释放单个对象的内存
    delete[] arrayPtr;  // 释放数组的内存
    singlePtr = nullptr;  // 将指针置为空,避免悬挂指针
    arrayPtr = nullptr;  // 将指针置为空,避免悬挂指针

    return 0;
}

内存泄漏检测方法

内存泄漏是程序中未释放的动态分配的内存。内存泄漏会导致程序占用越来越多的内存,最终可能导致程序崩溃或系统资源耗尽。

内存泄漏的常见原因

  • 忘记释放分配的内存。
  • 多次释放同一块内存。
  • 对于数组,使用 delete 而不是 delete[]

检测内存泄漏的方法

  • 使用内存泄漏检测工具,如 Valgrind、AddressSanitizer。
  • 手动跟踪内存分配和释放。

下面是一个示例,展示了如何使用 Valgrind 工具检测内存泄漏:

#include <iostream>

int main() {
    int* ptr = new int;  // 分配一个整数类型的内存
    *ptr = 10;

    std::cout << "Value in dynamically allocated memory: " << *ptr << std::endl;

    // 忘记释放内存
    return 0;
}

在命令行中,使用 Valgrind 工具运行程序:

valgrind ./your_program

Valgrind 会输出内存泄漏的报告,帮助您找到未释放的内存。

通过以上内容,我们了解了C++中的内存管理技术,包括动态内存分配与释放、newdelete 运算符,以及内存泄漏检测方法。良好的内存管理可以提高程序的性能和稳定性。

面向对象高级设计模式

面向对象编程中,设计模式是一种解决特定问题的通用方案。这些模式在实际编程中被广泛使用,可以提高代码的可维护性、可扩展性和可重用性。本节将详细介绍三种常见的面向对象高级设计模式:单例模式、观察者模式和工厂模式。

单例模式

单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于需要全局访问对象的情况,如配置管理器、日志记录器等。

单例模式的基本结构

  • 将构造函数声明为私有,防止外部直接创建实例。
  • 定义一个静态成员变量来保存唯一的实例。
  • 提供一个静态成员函数来获取实例。

下面是一个示例,展示了如何实现单例模式:

#include <iostream>

class Singleton {
private:
    static Singleton* instance;
    Singleton() {}  // 私有构造函数,防止外部直接创建实例

public:
    static Singleton* getInstance() {
        if (!instance) {
            instance = new Singleton();
        }
        return instance;
    }

    void showMessage() {
        std::cout << "Singleton instance message." << std::endl;
    }
};

// 初始化静态成员变量
Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* singletonInstance = Singleton::getInstance();
    singletonInstance->showMessage();

    Singleton* singletonInstance2 = Singleton::getInstance();
    singletonInstance2->showMessage();  // 应该与第一个实例相同

    return 0;
}

观察者模式

观察者模式允许一个对象(观察者)监听另一个对象(主题)的状态变化,并在状态变化时做出响应。这种模式常用于实现事件处理、订阅发布系统等。

观察者模式的基本结构

  • 主题(Subject):维护一个观察者列表,并提供订阅和取消订阅的方法。
  • 观察者(Observer):当主题状态变化时,观察者做出相应的反应。

下面是一个示例,展示了如何实现观察者模式:

#include <iostream>
#include <list>

class Observer {
public:
    virtual void update() = 0;  // 虚函数,观察者需要实现此方法
};

class ConcreteObserver : public Observer {
private:
    std::string name;

public:
    ConcreteObserver(const std::string& name) : name(name) {}

    void update() override {
        std::cout << name << " received update." << std::endl;
    }
};

class Subject {
private:
    std::list<Observer*> observers;

public:
    void attach(Observer* observer) {
        observers.push_back(observer);
    }

    void detach(Observer* observer) {
        observers.remove(observer);
    }

    void notify() {
        for (Observer* observer : observers) {
            observer->update();
        }
    }
};

int main() {
    Subject subject;
    ConcreteObserver observer1("Observer 1");
    ConcreteObserver observer2("Observer 2");

    subject.attach(&observer1);
    subject.attach(&observer2);

    subject.notify();  // 通知观察者

    subject.detach(&observer1);
    subject.notify();  // 再次通知观察者,观察者1不再收到通知

    return 0;
}

工厂模式

工厂模式用于将对象的创建和使用分离。工厂模式提供一个接口来创建对象,但允许子类决定实际创建的对象类型。这种模式常用于需要创建多个相似对象的情况。

工厂模式的基本结构

  • 抽象工厂(Abstract Factory):定义创建对象的接口,但不具体创建对象。
  • 具体工厂(Concrete Factory):实现创建具体对象的逻辑。
  • 产品(Product):具体对象的定义。

下面是一个示例,展示了如何实现工厂模式:

#include <iostream>

// 抽象产品
class Product {
public:
    virtual void use() = 0;  // 虚函数,产品需要实现此方法
    virtual ~Product() {}
};

// 具体产品1
class ConcreteProduct1 : public Product {
public:
    void use() override {
        std::cout << "ConcreteProduct1 is used." << std::endl;
    }
};

// 具体产品2
class ConcreteProduct2 : public Product {
public:
    void use() override {
        std::cout << "ConcreteProduct2 is used." << std::endl;
    }
};

// 抽象工厂
class AbstractFactory {
public:
    virtual Product* createProduct() = 0;
    virtual ~AbstractFactory() {}
};

// 具体工厂1
class ConcreteFactory1 : public AbstractFactory {
public:
    Product* createProduct() override {
        return new ConcreteProduct1();
    }
};

// 具体工厂2
class ConcreteFactory2 : public AbstractFactory {
public:
    Product* createProduct() override {
        return new ConcreteProduct2();
    }
};

int main() {
    AbstractFactory* factory1 = new ConcreteFactory1();
    Product* product1 = factory1->createProduct();
    product1->use();
    delete product1;

    AbstractFactory* factory2 = new ConcreteFactory2();
    Product* product2 = factory2->createProduct();
    product2->use();
    delete product2;

    delete factory1;
    delete factory2;

    return 0;
}

通过以上内容,我们了解了单例模式、观察者模式和工厂模式。这些设计模式可以提高代码的可维护性、可扩展性和可重用性。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消