文章目录
- 一、C++中的构造函数
- 二、C++中的析构函数
- 三、两者的配合与注意事项
- 四、C++中的静态成员变量
- 五、C++中的静态成员函数
- 六、C++中普通成员函数和静态成员函数的区别
- 七、C++中的const成员变量
- 八、C++中的const 成员函数
- 九、C++中构造函数的初始化列表
- 十、C++中的浅拷贝操作
- 十一、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: 20return 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: 42return 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: 100return 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 -> bstd::cout << "a: " << a << ", b: " << b << std::endl;}
};int main() {MyClass obj(1, 2); // 输出: a: 1, b: 2return 0;
}
十、C++中的浅拷贝操作
浅拷贝(Shallow Copy)是C++中对象复制的一种方式,它复制对象中的非动态成员或指针成员的地址,而不是指针指向的实际内容。这种方式简单且高效,但可能带来潜在的问题,例如多个对象共享同一块内存区域时的资源冲突。
浅拷贝的特点:
- 成员逐位复制: 浅拷贝直接将源对象的所有成员逐位复制到目标对象,包括指针的地址。
- 效率高: 浅拷贝通常只需完成简单的内存操作,无需额外的深层处理。
- 潜在问题: 由于共享了相同的动态内存,可能导致双重释放或数据不一致等问题。
浅拷贝一般由编译器自动生成的拷贝构造函数或赋值运算符完成,例如:
#include <iostream>
#include <cstring> // For std::strcpyclass 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::strcpyclass 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
深拷贝与浅拷贝的比较