浅拷贝 指向同一个地址空间
右边不可取地址 左边一定是到了具体的位置
右值引用std:: move
相信大家默认构造函数都没有问题,所以就不贴例子了
浅拷贝构造函数
只负责复制地址,而不是真的把完整的内存给它
#include <iostream>
// 浅拷贝是通过默认的复制构造函数来实现的。默认的复制构造函数会逐个复制对象的所有成员变量,但不会复制动态分配的内存。浅拷贝只是复制指针而不复制指针指向的内容。
class Shallow {
public:
int* data;
// 构造函数
Shallow(int d) {
data = new int(d);
}
// 浅拷贝构造函数
Shallow(const Shallow& source) : data(source.data) {
std::cout << "Shallow copy constructor - shallow copy" << std::endl;
}
// 析构函数
~Shallow() {
delete data;
std::cout << "Destructor freeing data" << std::endl;
}
};
int main() {
Shallow obj1(42);
Shallow obj2 = obj1; // 使用浅拷贝构造函数
std::cout << "obj1 data: " << *obj1.data << std::endl;
std::cout << "obj2 data: " << *obj2.data << std::endl;
return 0;
}
深拷贝构造函数
其实跟深拷贝的意思一样,真正的什么都复制
还要分配新的内存
#include <iostream>
class Deep {
public:
int* data;
// 构造函数
Deep(int d) {
data = new int(d);
}
// 深拷贝构造函数
Deep(const Deep& source) {
data = new int(*source.data);
std::cout << "Deep copy constructor - deep copy" << std::endl;
}
// 析构函数
~Deep() {
delete data;
std::cout << "Destructor freeing data" << std::endl;
}
};
int main() {
Deep obj1(42);
Deep obj2 = obj1; // 使用深拷贝构造函数
std::cout << "obj1 data: " << *obj1.data << std::endl;
std::cout << "obj2 data: " << *obj2.data << std::endl;
return 0;
}
浅拷贝构造函数的代码会遇到一个问题,就是出现悬空指针,析构的时候会指向同一个指针
而深拷贝就避免了这个问题
为了提升性能,因此就来了新的概念
移动语义
用移动构造函数和移动赋值运算符。
移动构造函数(Move Constructor)是C++11引入的一种特殊构造函数,
用于实现对象资源的高效转移,而不是复制。
它的引入是为了避免不必要的深拷贝,提高程序性能,特别是当处理临时对象时。
移动构造函数的概念
- 移动构造函数允许从一个即将销毁的对象(通常是临时对象)中“窃取”资源,而不是复制资源。
- 移动构造函数接收一个右值引用参数,即将被移动的对象,并将其资源转移到新对象中。
- 移动构造函数通常与移动赋值运算符一起使用,以实现全面的移动语义。
这里建议直接看例子
移动构造函数的语法
移动构造函数的声明使用右值引用参数(通常是 Type&&
),并在实现中转移资源。
#include <iostream>
class Mstring {
public:
char* data;
// 默认构造函数
Mstring() : data(nullptr) {
std::cout << "Default constructor called" << std::endl;
}
// 参数构造函数
Mstring(const char* str) {
if (str) {
data = new char[strlen(str) + 1];
strcpy(data, str);
} else {
data = nullptr;
}
std::cout << "Parameterized constructor called" << std::endl;
}
// 拷贝构造函数
Mstring(const Mstring& source) {
if (source.data) {
data = new char[strlen(source.data) + 1];
strcpy(data, source.data);
} else {
data = nullptr;
}
std::cout << "Copy constructor called" << std::endl;
}
// 移动构造函数
Mstring(Mstring&& source) noexcept : data(source.data) {
source.data = nullptr;
std::cout << "Move constructor called" << std::endl;
}
// 拷贝赋值运算符
Mstring& operator=(const Mstring& source) {
if (this == &source)
return *this;
delete[] data;
if (source.data) {
data = new char[strlen(source.data) + 1];
strcpy(data, source.data);
} else {
data = nullptr;
}
std::cout << "Copy assignment operator called" << std::endl;
return *this;
}
// 移动赋值运算符
Mstring& operator=(Mstring&& source) noexcept {
if (this == &source)
return *this;
delete[] data;
data = source.data;
source.data = nullptr;
std::cout << "Move assignment operator called" << std::endl;
return *this;
}
// 析构函数
~Mstring() {
delete[] data;
std::cout << "Destructor called" << std::endl;
}
};
int main() {
Mstring str1("Hello");
Mstring str2("World");
// 使用移动赋值运算符
str2 = std::move(str1);
std::cout << "After move assignment, str1: " << (str1.data ? str1.data : "nullptr") << std::endl;
std::cout << "After move assignment, str2: " << (str2.data ? str2.data : "nullptr") << std::endl;
return 0;
}
参数构造函数
参数构造函数是C++类的一种构造函数,
它接受一个或多个参数,
并使用这些参数来初始化类的成员变量。
与默认构造函数(不接受任何参数)相对,
参数构造函数为类的实例提供了一种灵活的初始化方式。
参数构造函数的定义
参数构造函数是在类的定义中声明的构造函数,
其参数列表可以包含一个或多个参数。
通过这些参数,构造函数可以对类的成员变量进行初始化。上面的例子是有体现的
- 默认构造函数:
Mstring()
,不接受任何参数,初始化成员变量data
为nullptr
。 - 参数构造函数:
Mstring(const char* str)
,接受一个const char*
参数,用于初始化成员变量data
。如果参数不为空,则分配足够的内存并复制字符串内容。 - 拷贝构造函数:
Mstring(const Mstring& source)
,用于创建一个现有对象的副本,分配新的内存并复制内容。 - 移动构造函数:
Mstring(Mstring&& source) noexcept
,用于高效地从另一个即将销毁的对象中移动资源,避免了不必要的内存分配和复制。 - 拷贝赋值运算符:用于将一个对象的内容赋值给另一个现有对象,确保正确地处理动态分配的内存。
- 移动赋值运算符:用于将一个即将销毁的对象的内容移动到另一个现有对象,避免不必要的内存分配和复制。
- 析构函数:
~Mstring()
,释放动态分配的内存,避免内存泄漏。 main
函数:演示如何使用参数构造函数创建对象,以及如何使用移动构造函数进行对象移动。
内存提前分配
内存提前分配(Pre-allocation)是指在对象或数据结构初始化时提前分配所需的内存,以减少运行时的分配开销。这在需要处理大量数据或频繁分配和释放内存的场景中特别有用。
参数构造函数中的内存提前分配示例
下面是一个改进的 Mstring
类,展示了如何在参数构造函数中提前分配内存。
#include <iostream>
class Array {
private:
int* data;
size_t size;
public:
// 参数构造函数:提前分配内存
Array(size_t size) : size(size) {
data = new int[size]; // 提前分配内存
std::cout << "Constructor: Allocated memory for " << size << " integers." << std::endl;
}
// 析构函数:释放内存
~Array() {
delete[] data;
std::cout << "Destructor: Freed allocated memory." << std::endl;
}
// 获取数组大小
size_t getSize() const {
return size;
}
// 获取数组元素(带边界检查)
int getElement(size_t index) const {
if (index < size) {
return data[index];
} else {
throw std::out_of_range("Index out of range");
}
}
// 设置数组元素(带边界检查)
void setElement(size_t index, int value) {
if (index < size) {
data[index] = value;
} else {
throw std::out_of_range("Index out of range");
}
}
};
int main() {
// 使用参数构造函数创建一个大小为10的数组
Array arr(10);
// 设置数组中的元素
for (size_t i = 0; i < arr.getSize(); ++i) {
arr.setElement(i, i * 2);
}
// 获取并打印数组中的元素
for (size_t i = 0; i < arr.getSize(); ++i) {
std::cout << "Element at index " << i << ": " << arr.getElement(i) << std::endl;
}
// arr对象超出作用域时,会自动调用析构函数释放内存
return 0;
}
内存重复利用
1.分配一块内存2构建对象3使用对象4析构对象5销毁内存
1.优先使用dowhile循环
压栈的开销非常大,因此避免重复调用
面向对象和面向过程的区别
面向过程和面向对象的区别-CSDN博客
什么是内联函数
函数移除不必要的多态性 减省空间
性能在面向对象中最顶的
swich 跳转很快
简化表达式+位移算法
IO优化
Vector
list
set/multiset
map/Multiset
选择策略
性能分析工具
Asan
实现原理: 编译的时候。。。方式
Gprof
Valgrind
Perf
能看到函数调用次数
系统资源对性能的影响
进程选取
更新当前任务虚拟时间