C++知识点汇总

1. 类与对象

是定义对象属性和行为的模板。它是一种抽象的数据类型,用于创建具体的实例(对象)。
对象 是类的实例,通过类定义创建。对象拥有类中定义的属性和方法。
示例代码:

#include <iostream>
using namespace std;

// 定义一个类
class Car {
public:
    string brand;
    string model;
    int year;

    // 类的方法
    void start() {
        cout << "Car is starting" << endl;
    }
};

int main() {
    // 创建一个对象
    Car myCar;
    myCar.brand = "Toyota";
    myCar.model = "Corolla";
    myCar.year = 2015;

    // 调用对象的方法
    myCar.start();
    return 0;
}

2. 继承

继承 允许一个类(子类)继承另一个类(父类)的特性(属性和方法)。
支持代码重用,并实现层次模型。
示例代码:

class Vehicle {
public:
    string brand = "Ford";

    void honk() {
        cout << "Tuut, tuut!" << endl;
    }
};

// 派生类
class Car: public Vehicle {
public:
    string model = "Mustang";
};

int main() {
    Car myCar;
    myCar.honk();
    cout << myCar.brand + " " + myCar.model;
    return 0;
}

3. 多态

多态 允许我们使用统一的接口访问不同的基础形态(类)。
通常通过继承和虚函数实现。
示例代码:

class Animal {
public:
    // 虚函数
    virtual void makeSound() {
        cout << "Some sound" << endl;
    }
};

class Pig : public Animal {
public:
    void makeSound() override {
        cout << "Oink oink" << endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() override {
        cout << "Bark bark" << endl;
    }
};

int main() {
    Animal* myAnimal = new Pig();
    myAnimal->makeSound();

    myAnimal = new Dog();
    myAnimal->makeSound();

    delete myAnimal;
    return 0;
}

4. 封装

封装 指将对象的数据(属性)和方法(行为)捆绑在一起,隐藏内部实现细节,只暴露必要的接口。
通过访问修饰符(如 public, private, protected)实现。
示例代码:

class BankAccount {
private:
    double balance; // 私有属性

public:
    BankAccount(double initialBalance) : balance(initialBalance) {}

    // 公有方法来访问私有属性
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    void withdraw(double amount) {
        if (amount <= balance) {
            balance -= amount;
        }
    }

    double getBalance() {
        return balance;
    }
};

int main() {
    BankAccount account(100.0);
    account.deposit(50.0);
    account.withdraw(25.0);

    cout << "Current balance: $" << account.getBalance();
    return 0;
}

这些是面向对象编程的四个核心概念。它们构成了 OOP 的基础。

5.深拷贝和浅拷贝

浅拷贝示例:

#include <iostream>
using namespace std;

class ShallowCopy {
public:
    int *data;

    ShallowCopy(int d) {
        data = new int(d);
    }

    // 浅拷贝构造函数
    ShallowCopy(const ShallowCopy &source) : data(source.data) {}

    ~ShallowCopy() {
        delete data;
    }
};

int main() {
    ShallowCopy obj1(100);
    ShallowCopy obj2 = obj1;  // 浅拷贝发生

    cout << *obj1.data << endl;  // 输出 100
    cout << *obj2.data << endl;  // 输出 100,但这是相同的内存地址

    return 0;
}

深拷贝示例:

#include <iostream>
using namespace std;

class DeepCopy {
public:
    int *data;

    DeepCopy(int d) {
        data = new int(d);
    }

    // 深拷贝构造函数
    DeepCopy(const DeepCopy &source) {
        data = new int(*source.data);
    }

    ~DeepCopy() {
        delete data;
    }
};

int main() {
    DeepCopy obj1(100);
    DeepCopy obj2 = obj1;  // 深拷贝发生

    cout << *obj1.data << endl;  // 输出 100
    cout << *obj2.data << endl;  // 输出 100,这是不同的内存地址

    return 0;
}

在例子中,ShallowCopy 类包含一个指向 int 的指针。当进行浅拷贝时,指针本身被复制,但指针指向的内存地址不变。这意味着新对象 obj2 和原对象 obj1 的 data 成员指向同一块内存区域。这种情况下,如果一个对象被销毁(比如离开作用域时),它会释放 data 指针指向的内存。如果另一个对象尝试访问同一块内存,就会出现问题如悬垂指针或程序崩溃因为内存已经被释放

在实际应用中,这就是为什么深拷贝通常比浅拷贝更安全,尤其是在涉及指针和动态内存分配时。通过深拷贝,每个对象都会拥有指向内容的独立副本,从而避免了上述问题。在实现深拷贝时,需要在拷贝构造函数中显式地为指针成员分配新的内存,并复制原始对象所指向的内容。

这里还涉及拷贝构造函数的知识点:

  1. 拷贝构造函数用于创建一个新对象作为另一个现有对象的副本;
  2. 它通常有一个参数,即对另一对象的引用;
  3. 如果没有显式定义拷贝构造函数,编译器会提供一个默认的,默认的拷贝构造函数为浅拷贝。

拷贝构造函数示例:

#include <iostream>
using namespace std;

class MyClass {
public:
    int a;

    // 默认构造函数
    MyClass() : a(0) {}

    // 参数化构造函数
    MyClass(int x) : a(x) {}

    // 拷贝构造函数
    MyClass(const MyClass &obj) {
        a = obj.a;
    }

    void display() {
        cout << "Value of a: " << a << endl;
    }
};

int main() {
    MyClass obj1(10);  // 使用参数化构造函数
    MyClass obj2 = obj1;  // 使用拷贝构造函数

    obj1.display();  // 输出: Value of a: 10
    obj2.display();  // 输出: Value of a: 10

    return 0;
}

6.什么是友元函数?它如何影响封装?

首先需要知道为什么要设计友元函数。

  1. 解决的问题
    提供访问权限:有时,你可能需要让某个函数能够访问类的私有成员,但又不想将这些成员设置为公共(public)。
    操作符重载:在实现操作符重载时,经常需要访问类的私有数据。
  2. 使用场景
    当函数不是类的逻辑成员,但需要访问类的私有或保护成员时。
    在操作符重载中,如重载输入输出操作符(<< 和 >>)。

其次什么是友元函数。友元函数是定义在类外部,但有权访问类的所有私有和保护成员的函数。它通常用于实现类与非成员函数之间的操作符重载等。虽然友元函数有助于提高灵活性,但它也破坏了类的封装性,因为它允许外部函数访问私有成员。

class MyClass {
private:
    int value;

public:
    MyClass(int v) : value(v) {}

    friend void displayValue(const MyClass &obj);
};

void displayValue(const MyClass &obj) {
    cout << obj.value << endl;
}

int main() {
    MyClass obj(10);
    displayValue(obj);  // 可以访问 MyClass 的私有成员
    return 0;
}

示例 :友元函数用于操作符重载
在下面的例子中,我们将使用友元函数来重载 << 操作符,以便能够直接打印 Point 类的对象。

#include <iostream>
using namespace std;

class Point {
private:
    int x, y;

public:
    Point(int x, int y) : x(x), y(y) {}

    // 友元函数声明
    friend ostream& operator<<(ostream& os, const Point& pt);
};

// 友元函数定义,重载 << 操作符
ostream& operator<<(ostream& os, const Point& pt) {
    os << "(" << pt.x << ", " << pt.y << ")";
    return os;
}

int main() {
    Point p1(2, 3);
    cout << p1 << endl;  // 输出: (2, 3)
    return 0;
}

示例 2: 多个类互为友元
在这个例子中,我们有两个类 ClassA 和 ClassB,它们互相需要访问对方的私有成员。

#include <iostream>
using namespace std;

class ClassB;  // 前向声明

class ClassA {
private:
    int valueA;

public:
    ClassA() : valueA(5) {}

    // ClassB 是 ClassA 的友元
    friend void showData(ClassA&, ClassB&);
};

class ClassB {
private:
    int valueB;

public:
    ClassB() : valueB(10) {}

    // ClassA 是 ClassB 的友元
    friend void showData(ClassA&, ClassB&);
};

// 友元函数定义
void showData(ClassA& a, ClassB& b) {
    cout << "Value of ClassA: " << a.valueA << endl;
    cout << "Value of ClassB: " << b.valueB << endl;
}

int main() {
    ClassA objA;
    ClassB objB;
    showData(objA, objB);
    return 0;
}

示例 3: 友元类
一个类可以将另一个类声明为其友元,这允许友元类访问该类的所有私有和保护成员。

#include <iostream>
using namespace std;

class ClassB;  // 前向声明

class ClassA {
private:
    int data;

public:
    ClassA() : data(100) {}

    friend class ClassB;  // 声明 ClassB 为友元类
};

class ClassB {
public:
    void showData(ClassA& a) {
        cout << "ClassA data: " << a.data << endl;
    }
};

int main() {
    ClassA objA;
    ClassB objB;
    objB.showData(objA);
    return 0;
}

为什么构造函数不能是虚函数?以及什么是虚表?

构造函数不可以是虚函数,因为当构造函数被调用时,虚表(用于支持虚函数)可能还没有建立。虚函数依赖于虚表来正确工作,因此在构造时无法使用虚函数。
虚表(Virtual Table)的详细解释
在 C++ 中,虚表是实现运行时多态的一种机制。当类中包含虚函数时,编译器会为这个类创建一个虚表。

  1. 虚表的概念
    虚表是一个存储类的虚函数地址的表格。
    每个包含虚函数的类都有其自己的虚表。
    类的每个对象都包含一个指向相应虚表的指针,称为虚指针(vptr)。
    当虚函数被调用时,程序会使用虚指针和虚表来确定应该调用哪个函数。
  2. 虚表的作用
    虚表允许运行时多态,即在运行时确定调用哪个函数。
    它支持动态绑定,即基于对象的实际类型而非其声明类型来调用方法。
  3. 虚表如何工作
    编译时,对于包含虚函数的类,编译器会创建一个虚表。
    类的每个对象都会包含一个指向虚表的指针。
    当调用虚函数时,程序会查找虚指针,进而查找虚表,最终通过虚表找到正确的函数地址。
  4. 虚表的例子
class Base {
public:
    virtual void func1() {
        cout << "Base func1" << endl;
    }
    virtual void func2() {
        cout << "Base func2" << endl;
    }
};

class Derived : public Base {
public:
    void func1() override {
        cout << "Derived func1" << endl;
    }
};

在这个例子中,Base 和 Derived 类都有各自的虚表。当通过 Base 类型的指针或引用调用 func1 时,程序会检查虚表以确定是否调用 Base 的 func1 还是 Derived 的 func1。

虚表的工作原理
初始化:当一个对象被创建时,如果它属于包含虚函数的类或其派生类,则会在内存中为它分配一个虚指针(vptr)。这个指针指向相关类的虚表。
虚表结构:虚表是一个数组,每个元素都是一个函数指针,指向类的一个虚函数。这些函数指针按照它们在类中声明的顺序排列。
动态绑定:当调用一个虚函数时,编译器会通过对象的虚指针查找虚表,从而找到对应的函数实现来执行。
为何使用虚表
支持多态:虚表允许在运行时而非编译时确定应调用哪个函数。这是实现多态的关键
灵活性:派生类可以重写基类的虚函数。通过虚表,可以确保调用的是对象的实际类型对应的函数实现。
示例
考虑一个包含虚函数的基类和派生类。当创建派生类对象并通过基类指针调用虚函数时,虚表机制确保调用的是派生类中重写的版本。

7.描述 C++ 中的静态成员变量和静态成员函数

静态成员变量是属于类的,而不是属于类的某个特定对象的变量。
静态成员函数也是类的一部分,它可以访问类的静态成员变量,但不能访问普通成员变量或函数。
静态成员在所有对象之间共享,可以在没有任何对象实例的情况下访问。
示例:

#include <iostream>
using namespace std;

class MyClass {
public:
    static int staticValue;  // 静态成员变量

    static void staticMethod() {  // 静态成员函数
        cout << "Static Method. Static Value is " << staticValue << endl;
    }
};

int MyClass::staticValue = 100;  // 初始化静态成员变量

int main() {
    MyClass::staticValue = 200;
    MyClass::staticMethod();  // 调用静态方法

    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值