C++中类和对象的细节原理

一、C++中的构造函数

1.构造函数的定义和作用
构造函数是与类同名的特殊函数,在创建类的对象时自动调用,用于对对象进行初始化。

2.构造函数的特点

  • 与类同名:构造函数的名称必须与类名一致。
  • 无返回值:构造函数没有返回值(即使是 void 也不能写)。
  • 自动调用:在对象创建时自动调用,无需显式调用。
  • 支持重载:可以定义多个构造函数,参数列表不同即可(构成重载)。
  • 可以使用初始化列表:常用于初始化 const 数据成员或引用类型成员。

3.构造函数的类型
默认构造函数:无参数的构造函数。

class MyClass {
public:
    MyClass() { // 默认构造函数
        std::cout << "Default constructor called!" << std::endl;
    }
};
MyClass obj; // 自动调用默认构造函数

参数化构造函数:包含参数的构造函数。

class MyClass {
private:
    int x;
public:
    MyClass(int val) : x(val) { // 使用初始化列表
        std::cout << "Parameterized constructor called!" << std::endl;
    }
};
MyClass obj(10); // 调用参数化构造函数

拷贝构造函数:用于通过同类型对象初始化新对象。

class MyClass {
private:
    int x;
public:
    MyClass(int val) : x(val) {}
    MyClass(const MyClass& other) : x(other.x) { // 拷贝构造函数
        std::cout << "Copy constructor called!" << std::endl;
    }
};
MyClass obj1(10);
MyClass obj2 = obj1; // 调用拷贝构造函数

移动构造函数(C++11 引入):用于移动资源而不是复制资源,减少性能开销。

class MyClass {
private:
    int* data;
public:
    MyClass(int val) : data(new int(val)) {}
    MyClass(MyClass&& other) noexcept : data(other.data) { // 移动构造函数
        other.data = nullptr;
        std::cout << "Move constructor called!" << std::endl;
    }
    ~MyClass() { delete data; }
};
MyClass obj1(10);
MyClass obj2 = std::move(obj1); // 调用移动构造函数

二、C++中的析构函数

1.析构函数的定义和作用
析构函数是与类同名并以 ~ 开头的特殊函数,在对象生命周期结束时自动调用,用于释放资源或执行清理操作。

2.析构函数的特点

  • 与类同名并以 ~ 开头。
  • 无参数、无返回值。
  • 自动调用:在对象超出作用域或被显式删除时自动调用。
  • 不能被重载:每个类只能有一个析构函数。
class MyClass {
public:
    MyClass() { std::cout << "Constructor called!" << std::endl; }
    ~MyClass() { std::cout << "Destructor called!" << std::endl; }
};
int main() {
    MyClass obj; // 创建对象时调用构造函数
} // 作用域结束时,自动调用析构函数

3.析构函数的常见用途
释放动态内存:

class MyClass {
private:
    int* data;
public:
    MyClass(int val) : data(new int(val)) {}
    ~MyClass() {
        delete data; // 释放动态内存
        std::cout << "Destructor called and memory freed!" << std::endl;
    }
};

三、两者的配合与注意事项

构造和析构的匹配:每次调用构造函数分配资源时,析构函数都应该负责释放资源(遵循 RAII 原则)。
虚析构函数:如果类包含虚函数,析构函数也应声明为 virtual,以确保通过基类指针删除派生类对象时,派生类的析构函数能被正确调用。

class Base {
public:
    virtual ~Base() { std::cout << "Base destructor called!" << std::endl; }
};
class Derived : public Base {
public:
    ~Derived() { std::cout << "Derived destructor called!" << std::endl; }
};
Base* obj = new Derived();
delete obj; // 调用 Derived 和 Base 的析构函数

四、C++中的静态成员变量

静态成员变量是用 static 关键字声明的类成员变量。它是类的公共成员,在类的所有对象之间共享。
静态成员变量的特点:

  • 属于类,而非对象:静态成员变量只占用一份内存,不随对象的创建而分配,也不会随对象的销毁而释放。
  • 共享性:所有类的对象共享同一个静态成员变量。
  • 必须在类外定义并初始化(除非是 constexpr 静态变量)。
  • 存储周期:程序启动时分配内存,程序结束时释放。
  • 访问方式:可以通过对象或类名访问(推荐通过类名访问)。

类中静态成员变量的声明与定义:

#include <iostream>
class MyClass {
public:
    static int count; // 静态成员变量声明

    MyClass() {
        count++; // 每次创建对象时增加计数
    }
};

// 静态成员变量必须在类外定义并初始化
int MyClass::count = 0;

int main() {
    MyClass obj1, obj2, obj3;
    std::cout << "Total objects created: " << MyClass::count << std::endl;
    return 0;
}

C++11 引入的 constexpr 允许静态成员变量在类内定义,但必须是常量且编译时可确定值。

#include <iostream>
class MyClass {
public:
    static constexpr int maxObjects = 10; // 类内定义并初始化
};

int main() {
    std::cout << "Maximum objects allowed: " << MyClass::maxObjects << std::endl;
    return 0;
}

五、C++中的静态成员函数

静态成员函数是用 static 修饰的类成员函数。它是类级别的行为,与具体对象无关。
静态成员函数的特点:

  • 不依赖对象:可以通过类名直接调用,无需实例化对象。
  • 只能访问静态成员:静态函数不能访问非静态成员变量或调用非静态成员函数。
  • 共享性:静态成员函数在类的所有对象之间共享。
  • 作用域限制:只能访问与类相关的静态数据或静态成员。

静态成员函数的基本用法:

#include <iostream>
class MyClass {
public:
    static int count; // 静态成员变量
    static void printCount() { // 静态成员函数
        std::cout << "Total objects created: " << count << std::endl;
    }
};

int MyClass::count = 0;

int main() {
    MyClass::count = 5; // 直接通过类名访问静态成员变量
    MyClass::printCount(); // 直接通过类名调用静态成员函数
    return 0;
}

静态成员函数访问静态变量:

#include <iostream>

class MyClass {
private:
    static int count; // 静态成员变量
public:
    static void incrementCount() { count++; } // 修改静态成员变量
    static int getCount() { return count; }   // 获取静态成员变量
};

// 静态成员变量定义和初始化
int MyClass::count = 0;

int main() {
    MyClass::incrementCount(); // 调用静态函数
    MyClass::incrementCount();
    std::cout << "Count: " << MyClass::getCount() << std::endl;
    return 0;
}

六、C++中普通成员函数和静态成员函数的区别

在这里插入图片描述

七、C++中的const成员变量

const 成员变量的定义:

  • const 成员变量是类中的常量,其值一旦初始化后便不能更改。
  • const 成员变量通常需要在构造函数的初始化列表中进行初始化。

const 成员变量的特点:

  • 不可更改:一旦初始化,值不可修改。
  • 初始化方式:只能通过构造函数的初始化列表进行初始化,不能在构造函数的主体中赋值。
  • 作用范围:它的作用域与普通成员变量相同,但只能被读取。
#include <iostream>

class MyClass {
private:
    const int value; // const 成员变量
public:
    // 使用初始化列表对 const 成员变量初始化
    MyClass(int v) : value(v) {}

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

int main() {
    MyClass obj(42);
    obj.printValue(); // 输出 Value: 42

    // obj.value = 10; // 错误:无法修改 const 成员变量
    return 0;
}

八、C++中的const 成员函数

const 成员函数的定义:

  • const 成员函数是一个不会修改类中任何非静态成员变量的函数。
  • 在函数声明或定义后加 const 关键字来修饰该函数。

const 成员函数的特点:

  • 不能修改成员变量:在函数内不能更改任何非 mutable 的成员变量。
  • 只能调用其他 const 成员函数:在 const 成员函数中不能调用非 const 成员函数。
#include <iostream>

class MyClass {
private:
    int value;
public:
    MyClass(int v) : value(v) {}

    int getValue() const { // const 成员函数
        return value;
    }

    void setValue(int v) { // 非 const 成员函数
        value = v;
    }
};

int main() {
    const MyClass obj(10); // const 对象

    std::cout << "Value: " << obj.getValue() << std::endl; // 可以调用 const 成员函数

    // obj.setValue(20); // 错误:const 对象不能调用非 const 成员函数
    return 0;
}

普通对象也可以调用 const 成员函数,但 const 对象只能调用 const 成员函数。

int main() {
    MyClass obj(10); // 普通对象
    std::cout << "Value: " << obj.getValue() << std::endl; // 可以调用 const 成员函数

    obj.setValue(20); // 可以调用非 const 成员函数
    std::cout << "Updated Value: " << obj.getValue() << std::endl;
    return 0;
}

const 成员函数可以调用其他 const 成员函数,但不能调用非 const 成员函数。

#include <iostream>

class MyClass {
private:
    int value;
public:
    MyClass(int v) : value(v) {}

    int getValue() const { return value; }
    void printValue() const {
        std::cout << "Value: " << getValue() << std::endl; // 调用另一个 const 成员函数
    }
};

int main() {
    MyClass obj(42);
    obj.printValue();
    return 0;
}

如果类的成员变量用 mutable 修饰,则即使在 const 成员函数中也可以修改它。

#include <iostream>

class MyClass {
private:
    mutable int mutableValue; // 可变成员变量
public:
    MyClass(int v) : mutableValue(v) {}

    void modifyValue() const {
        mutableValue++; // 在 const 成员函数中修改 mutable 成员
        std::cout << "Mutable Value: " << mutableValue << std::endl;
    }
};

int main() {
    const MyClass obj(10);
    obj.modifyValue(); // 修改 mutable 成员变量
    return 0;
}

九、C++中构造函数的初始化列表

C++ 中,构造函数的初始化列表 是一种在对象构造时初始化类成员的方式。它提供了比在构造函数体内赋值更高效和灵活的初始化方式,特别是对于 const 成员、引用类型成员和基类的初始化至关重要。

1.初始化列表的语法
初始化列表紧跟构造函数的声明,使用冒号 : 开始,并列出成员变量或基类的初始化表达式。

class ClassName {
private:
    int member1;
    int member2;
public:
    // 构造函数使用初始化列表
    ClassName(int a, int b) : member1(a), member2(b) {
        // 构造函数体
    }
};

2.初始化列表的特点

效率高:

  • 初始化列表直接调用构造函数或赋值初始化成员变量,避免了默认构造和后续赋值的过程。
  • 对于复杂对象(如类类型成员),这种方式更高效。

适用性强:

  • 可以初始化const 成员变量。
  • 可以初始化引用类型成员变量。
  • 必须用于基类构造函数调用和成员对象的构造。

成员初始化顺序:

  • 成员的初始化顺序与它们在类中声明的顺序一致,而非在初始化列表中的顺序。
  • 建议按照声明顺序书写初始化列表,以避免潜在的错误和混淆。

3.普通成员变量的初始化

#include <iostream>

class MyClass {
private:
    int x;
    int y;
public:
    // 使用初始化列表初始化成员变量
    MyClass(int a, int b) : x(a), y(b) {}

    void print() {
        std::cout << "x: " << x << ", y: " << y << std::endl;
    }
};

int main() {
    MyClass obj(10, 20);
    obj.print(); // 输出: x: 10, y: 20
    return 0;
}

4.初始化 const 成员变量
const 成员变量必须在对象构造时完成初始化,且不能在构造函数体内赋值。

#include <iostream>
class MyClass {
private:
    const int value; // const 成员变量
public:
    MyClass(int v) : value(v) {} // 初始化列表初始化

    void print() {
        std::cout << "Value: " << value << std::endl;
    }
};

int main() {
    MyClass obj(42);
    obj.print(); // 输出: Value: 42
    return 0;
}

5.初始化引用成员变量
引用类型的成员变量必须通过初始化列表进行初始化

#include <iostream>
class MyClass {
private:
    int& ref; // 引用类型成员
public:
    MyClass(int& r) : ref(r) {}

    void print() {
        std::cout << "Reference value: " << ref << std::endl;
    }
};

int main() {
    int x = 100;
    MyClass obj(x);
    obj.print(); // 输出: Reference value: 100
    return 0;
}

6.初始化类类型成员变量
当类中包含其他类类型的成员时,这些成员的构造函数只能通过初始化列表调用。

#include <iostream>

class Member {
private:
    int value;
public:
    Member(int v) : value(v) {
        std::cout << "Member constructed with value: " << value << std::endl;
    }
};

class MyClass {
private:
    Member member; // 类类型成员
public:
    MyClass(int v) : member(v) {} // 初始化列表调用 Member 的构造函数
};

int main() {
    MyClass obj(42);
    return 0;
}

输出:

Member constructed with value: 42

7.初始化基类和虚基类
派生类构造函数必须通过初始化列表调用基类的构造函数,尤其是基类没有默认构造函数时。

#include <iostream>

class Base {
private:
    int value;
public:
    Base(int v) : value(v) {
        std::cout << "Base constructed with value: " << value << std::endl;
    }
};

class Derived : public Base {
public:
    Derived(int v) : Base(v) { // 初始化列表调用基类构造函数
        std::cout << "Derived constructed" << std::endl;
    }
};

int main() {
    Derived obj(42);
    return 0;
}

输出:

Base constructed with value: 42
Derived constructed

8.初始化列表的成员初始化顺序
成员变量的初始化顺序与它们在类中声明的顺序一致,而不是初始化列表的顺序。

#include <iostream>
class MyClass {
private:
    int a;
    int b;
public:
    MyClass(int x, int y) : b(y), a(x) { // 初始化顺序仍是 a -> b
        std::cout << "a: " << a << ", b: " << b << std::endl;
    }
};

int main() {
    MyClass obj(1, 2); // 输出: a: 1, b: 2
    return 0;
}

十、C++中的浅拷贝操作

浅拷贝(Shallow Copy)是C++中对象复制的一种方式,它复制对象中的非动态成员或指针成员的地址,而不是指针指向的实际内容。这种方式简单且高效,但可能带来潜在的问题,例如多个对象共享同一块内存区域时的资源冲突。

浅拷贝的特点:

  • 成员逐位复制: 浅拷贝直接将源对象的所有成员逐位复制到目标对象,包括指针的地址。
  • 效率高: 浅拷贝通常只需完成简单的内存操作,无需额外的深层处理。
  • 潜在问题: 由于共享了相同的动态内存,可能导致双重释放或数据不一致等问题。

浅拷贝一般由编译器自动生成的拷贝构造函数或赋值运算符完成,例如:

#include <iostream>
#include <cstring> // For std::strcpy

class ShallowCopyExample {
private:
    char* data;

public:
    // 构造函数
    ShallowCopyExample(const char* initValue) {
        data = new char[strlen(initValue) + 1];
        std::strcpy(data, initValue);
        std::cout << "Constructor: Allocated memory for " << data << std::endl;
    }

    // 默认拷贝构造函数(浅拷贝)
    ShallowCopyExample(const ShallowCopyExample& other) = default;

    // 默认赋值运算符(浅拷贝)
    ShallowCopyExample& operator=(const ShallowCopyExample& other) = default;

    // 打印数据
    void print() const {
        std::cout << "Data: " << data << std::endl;
    }

    // 析构函数
    ~ShallowCopyExample() {
        std::cout << "Destructor: Deleting memory for " << data << std::endl;
        delete[] data; // 如果多个对象共享同一块内存,这里会出错
    }
};

int main() {
    ShallowCopyExample obj1("Hello");
    ShallowCopyExample obj2 = obj1; // 调用浅拷贝构造函数
    obj2.print();

    return 0; // 退出时,可能发生双重释放错误
}

输出示例:

Constructor: Allocated memory for Hello
Data: Hello
Destructor: Deleting memory for Hello
Destructor: Deleting memory for Hello // 可能导致崩溃

十一、C++中的深拷贝操作

深拷贝(Deep Copy)是C++中一种对象复制策略,其核心思想是复制对象中的数据内容,而不是直接共享指针或引用的内存地址。深拷贝的目的是确保每个对象都有自己独立的资源,避免资源冲突或双重释放等问题。

深拷贝的特点:

  • 独立性: 深拷贝为新对象分配新的内存并复制数据,因此新旧对象互不干扰。
  • 安全性: 深拷贝避免了浅拷贝中常见的资源管理问题,如双重释放或数据污染。
  • 代价: 深拷贝通常比浅拷贝消耗更多的资源和时间,因为它需要分配新的内存并复制数据。

深拷贝的实现:

#include <iostream>
#include <cstring> // For std::strcpy

class DeepCopyExample {
private:
    char* data; // 动态分配的资源

public:
    // 构造函数
    DeepCopyExample(const char* initValue) {
        data = new char[strlen(initValue) + 1];
        std::strcpy(data, initValue);
        std::cout << "Constructor: Allocated memory for " << data << std::endl;
    }

    // 深拷贝构造函数
    DeepCopyExample(const DeepCopyExample& other) {
        data = new char[strlen(other.data) + 1];
        std::strcpy(data, other.data);
        std::cout << "Copy Constructor: Allocated new memory for " << data << std::endl;
    }

    // 深拷贝赋值运算符
    DeepCopyExample& operator=(const DeepCopyExample& other) {
        if (this != &other) { // 避免自我赋值
            delete[] data; // 释放已有内存
            data = new char[strlen(other.data) + 1];
            std::strcpy(data, other.data);
            std::cout << "Assignment Operator: Re-allocated memory for " << data << std::endl;
        }
        return *this;
    }

    // 打印数据
    void print() const {
        std::cout << "Data: " << data << std::endl;
    }

    // 析构函数
    ~DeepCopyExample() {
        std::cout << "Destructor: Deleting memory for " << data << std::endl;
        delete[] data;
    }
};

int main() {
    DeepCopyExample obj1("Hello");    // 构造对象
    DeepCopyExample obj2 = obj1;     // 调用拷贝构造函数
    obj2.print();

    DeepCopyExample obj3("World");
    obj3 = obj1;                     // 调用赋值运算符
    obj3.print();

    return 0;
}

输出结果:

Constructor: Allocated memory for Hello
Copy Constructor: Allocated new memory for Hello
Data: Hello
Constructor: Allocated memory for World
Assignment Operator: Re-allocated memory for Hello
Data: Hello
Destructor: Deleting memory for Hello
Destructor: Deleting memory for Hello
Destructor: Deleting memory for Hello

深拷贝与浅拷贝的比较
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值