目录
1. C++ 中的 const、new、delete 和 static
引言
在C++中,运算符重载和友元函数是面向对象编程的重要特性。通过运算符重载,我们可以为自定义类型定义运算符的行为;而友元函数则允许外部函数访问类的私有成员。本文将详细介绍赋值运算符重载和友元函数的概念、用法及实际应用。
1. C++ 中的 const
、new
、delete
和 static
1.1 const
关键字
const
用于定义常量或不可修改的变量。它可以修饰变量、函数参数、成员函数和返回值。
1.1.1 const
变量
const int MAX_VALUE = 100; // 定义一个常量
// MAX_VALUE = 200; // 错误:常量不可修改
1.1.2 const
成员函数
const
成员函数表示该函数不会修改类的成员变量。
class MyClass {
private:
int value;
public:
MyClass(int v) : value(v) {}
// const 成员函数
int getValue() const {
return value;
}
};
1.1.3 const
指针和引用
int a = 10;
const int* p1 = &a; // 指向常量的指针,不能通过 p1 修改 a
int* const p2 = &a; // 常量指针,p2 不能指向其他地址
const int& r = a; // 常量引用,不能通过 r 修改 a
1.2 new
和 delete
new
和 delete
用于动态内存管理。new
用于分配内存,delete
用于释放内存。
1.2.1 动态分配单个对象
int* p = new int(10); // 动态分配一个 int 类型的内存,并初始化为 10
std::cout << *p << std::endl; // 输出: 10
delete p; // 释放内存
1.2.2 动态分配数组
int* arr = new int[5]{1, 2, 3, 4, 5}; // 动态分配一个 int 数组
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
delete[] arr; // 释放数组内存
1.2.3 注意事项
- 使用
new
分配的内存必须用delete
释放,否则会导致内存泄漏。 - 使用
new[]
分配数组时,必须用delete[]
释放。
1.3 static
关键字
static
可以用于修饰变量、函数和类成员,具有不同的作用。
1.3.1 静态局部变量
静态局部变量的生命周期贯穿整个程序运行期间,但作用域仅限于函数内部。
void func() {
static int count = 0; // 静态局部变量
count++;
std::cout << "Count: " << count << std::endl;
}
int main() {
func(); // 输出: Count: 1
func(); // 输出: Count: 2
return 0;
}
1.3.2 静态成员变量
静态成员变量属于类,而不是类的某个对象。所有对象共享同一个静态成员变量。
class MyClass {
public:
static int count; // 静态成员变量声明
MyClass() {
count++;
}
};
int MyClass::count = 0; // 静态成员变量定义
int main() {
MyClass obj1;
MyClass obj2;
std::cout << "Count: " << MyClass::count << std::endl; // 输出: Count: 2
return 0;
}
1.3.3 静态成员函数
静态成员函数属于类,而不是类的某个对象。它只能访问静态成员变量。
class MyClass {
public:
static int count;
static void printCount() {
std::cout << "Count: " << count << std::endl;
}
};
int MyClass::count = 0;
int main() {
MyClass::printCount(); // 输出: Count: 0
return 0;
}
const
:用于定义常量或不可修改的变量、函数和指针。new
和delete
:用于动态内存管理,需成对使用以避免内存泄漏。static
:用于定义静态变量、静态成员变量和静态成员函数,具有全局生命周期或类作用域。
2. 赋值运算符重载
2.1 什么是赋值运算符重载?
赋值运算符(=
)用于将一个对象的值赋给另一个对象。对于内置类型,C++已经定义了默认的赋值行为。但对于自定义类型(如类或结构体),我们需要通过重载赋值运算符来实现特定的赋值逻辑。
2.2 赋值运算符重载的语法
赋值运算符重载函数的声明形式如下:
ClassName& operator=(const ClassName& other);
- 返回值:返回当前对象的引用(
*this
),以支持链式赋值(如a = b = c
)。 - 参数:通常是一个常量引用(
const ClassName&
),避免不必要的拷贝。 -
2.3 示例代码
以下是一个简单的类
MyString
,演示如何重载赋值运算符:
#include <iostream>
#include <cstring>
class MyString {
private:
char* str; // 动态分配的字符数组
public:
// 构造函数
MyString(const char* s = "") {
str = new char[strlen(s) + 1];
strcpy(str, s);
}
// 析构函数
~MyString() {
delete[] str;
}
// 赋值运算符重载
MyString& operator=(const MyString& other) {
if (this == &other) { // 自赋值检查
return *this;
}
delete[] str; // 释放原有资源
str = new char[strlen(other.str) + 1];
strcpy(str, other.str);
return *this;
}
// 打印字符串
void print() const {
std::cout << str << std::endl;
}
};
int main() {
MyString s1("Hello");
MyString s2;
s2 = s1; // 调用赋值运算符重载
s2.print(); // 输出: Hello
return 0;
}
2.4 注意事项
- 自赋值检查:在赋值运算符重载中,必须检查自赋值(如
a = a
),否则可能导致资源释放错误。 - 深拷贝与浅拷贝:如果类包含动态分配的资源(如指针),必须实现深拷贝,避免多个对象共享同一资源。
3. 友元函数
3.1 什么是友元函数?
友元函数(friend
)是一个非成员函数,但它可以访问类的私有成员和受保护成员。友元函数通常用于实现某些特殊功能,如运算符重载。
3.2 友元函数的语法
友元函数的声明形式如下:
friend ReturnType FunctionName(Parameters);
- 声明位置:友元函数在类内部声明,但定义可以在类外部。
- 访问权限:友元函数可以访问类的私有成员。
3.3 示例代码
以下是一个使用友元函数重载 <<
运算符的示例:
#include <iostream>
class MyString {
private:
char* str;
public:
// 构造函数
MyString(const char* s = "") {
str = new char[strlen(s) + 1];
strcpy(str, s);
}
// 析构函数
~MyString() {
delete[] str;
}
// 友元函数声明
friend std::ostream& operator<<(std::ostream& os, const MyString& obj);
};
// 友元函数定义
std::ostream& operator<<(std::ostream& os, const MyString& obj) {
os << obj.str; // 访问私有成员 str
return os;
}
int main() {
MyString s("Hello, CSDN!");
std::cout << s << std::endl; // 输出: Hello, CSDN!
return 0;
}
3.4 注意事项
- 慎用友元函数:友元函数破坏了封装性,应尽量避免滥用。
- 适用场景:友元函数通常用于运算符重载(如
<<
、>>
)或需要访问私有成员的全局函数。
4. 赋值运算符重载与友元函数的结合
4.1 结合使用的场景
在某些情况下,我们可能需要结合使用赋值运算符重载和友元函数。例如,实现一个类的加法运算符重载,并允许外部函数访问私有成员。
4.2 示例代码
以下是一个结合赋值运算符重载和友元函数的示例:
#include <iostream>
class Point {
private:
int x, y;
public:
// 构造函数
Point(int x = 0, int y = 0) : x(x), y(y) {}
// 赋值运算符重载
Point& operator=(const Point& other) {
if (this == &other) {
return *this;
}
x = other.x;
y = other.y;
return *this;
}
// 友元函数声明
friend Point operator+(const Point& p1, const Point& p2);
friend std::ostream& operator<<(std::ostream& os, const Point& p);
};
// 友元函数定义:加法运算符重载
Point operator+(const Point& p1, const Point& p2) {
return Point(p1.x + p2.x, p1.y + p2.y);
}
// 友元函数定义:输出运算符重载
std::ostream& operator<<(std::ostream& os, const Point& p) {
os << "(" << p.x << ", " << p.y << ")";
return os;
}
int main() {
Point p1(1, 2);
Point p2(3, 4);
Point p3 = p1 + p2; // 调用加法运算符重载
std::cout << p3 << std::endl; // 输出: (4, 6)
return 0;
}
5. 总结
- 赋值运算符重载:用于自定义类型的赋值行为,需注意自赋值和深拷贝问题。
- 友元函数:允许外部函数访问类的私有成员,常用于运算符重载。
- 结合使用:在某些场景下,赋值运算符重载和友元函数可以结合使用,实现更灵活的功能。
6. 参考
- 《C++ Primer》
- C++ 官方文档