cpp拾遗——构造, new, delete, 静态成员,友元,操作符重载
1. 构造
构造函数的调用
T t(1,2); // 调用 T(int ,int)
T t2 = (1,2); // 调用 T(int)
T t3 = 1; // 调用 T(int)
T t3 = T(1); // 调用 T(int) 构造匿名对象,并将 匿名对象 和符号 t3绑定
T t4 = t3; // 调用 T(T&)
T t5(t4); // 调用 T(T&)
void func(T t);
func(t5); // 调用 T(T&) 构造形参t
class T {
public:
T(int a); // 当定义了构造函数,编译器就不提供构造函数
}
T a; // 所以这里错误
1.1 构造函数初始化列表
当使用组合对象时,可能需要使用初始化列表
class A{
public:
A(int a);
}
class B {
public:
B(int a, int b);
A m_a;
}
B(1,2); // 这里会报错,由于构造B 时,会先构造A,但是无法调用A的有参构造。
// 所以必须改成
class B{
public:
B(int a, int b) : m_a2(a), m_a(b) {}
A m_a;
A m_a2;
}
组合对象的构造顺序和定义顺序有关,和初始化列表顺序无关。
析构顺序和构造顺序相反
若类有const属性,则必须用参数化列表进行初始化。
class A{
public:
A(int a) :m_a(a) {}
private:
const int m_a;
};
2. 匿名对象
函数返回对象时,会构造一个匿名对象,匿名对象是否被析构,取决于是否又符号去绑定。
T f() {
T a;
return T; // 调用 T(T&) 构造匿名对象
}
T t1 = f(); // 匿名对象又符号接,所以不会被析构
T t2;
t2 = f(); // 匿名对象没符号接,会被析构
3. new delete
3.1 基本用法
int *a = new int;
int *arr = new int[5];
delete a;
delete []arr;
3.2 new delete 和 malloc free
new delete 调用时意味构造析构调用,
对于基础类型,new delete 和 Malloc free 相同,所以可以交叉调用,
对于自定义类型,不能交叉调用。
4. 静态成员
4.1 基本使用
class T{
public:
// 函数或属性 使用 static 声明,则为静态类型
static int g_num;
// 静态成员函数内只能使用 静态成员属性,不能使用非静态成员属性
static int get_num()
{
return g_num;
}
};
// 静态成员属性存储在 .data 段,所以必须 在编译时 分配空间
int T::g_num =1;
4.2 静态成员的本质
使用如下代码反汇编
4 class T{
5 public:
6 static int g_num;
7 static int get_num()
8 {
9 return g_num;
10 }
11 };
12
13 class A{
14 public:
15 static int g_num;
16 static int get_num()
17 {
18 return g_num;
19 }
20 };
21
22 int T::g_num =1;
23 int A::g_num =1;
24 int g_num = 1;
25
26 static int get_num()
27 {
28 return g_num;
29 }
查看 g_num, T::g_num, A::g_num
>> 173 08049af4 g O .data 00000004 _ZN1T5g_numE
>> 189 08049afc g O .data 00000004 g_num
>> 190 08049af8 g O .data 00000004 _ZN1A5g_numE
>> 166 08048678 l F .text 0000000a _ZL7get_numv
>> 175 08048771 w F .text 0000000a _ZN1T7get_numEv
>> 197 0804877b w F .text 0000000a _ZN1A7get_numEv
说明 编译器对符号进行重命名以实现封装
T::g_num --> _ZN1T5g_numE
A::g_num --> _ZN1A5g_numE
g_num --> g_num
T::get_num() --> _ZN1T7get_numEv()
A::get_num() --> _ZN1A7get_numEv()
get_num() --> _ZL7get_numv()
所以以上cpp可用c描述成
22 // class T
23 static int T_g_num = 1;
24 static int T_get_num()
25 {
26 return T_g_num;
27 }
28
29 // class A
30 static int A_g_num = 1;
31 static int A_get_num()
32 {
33 return A_g_num;
34 }
35
36 // normal
37 static int g_num = 1;
38 static int get_num()
39 {
40 return g_num;
41 }
结论:
静态成员 本质 是全局函数和变量,无需定义对象,就能可以直接使用。
由于被cpp编译器重命名,所以必须用 加类前缀的方式调用。
4.3 静态成员函数中为什么不能使用普通成员
静态成员函数中使用 普通成员 会报错,如下
13 class A{
14 public:
15 static int g_num;
16 static int get_num()
17 {
18 f();
m_a++;
19 return g_num;
20 }
21 void f() { get_num(); m_a++; }
void f2() {f(); }
int m_a;
22 };
因为编译时普通成员没有被分配内存。
如上若翻译成c
16 static int A_get_num()
17 {
18 A_f(this); // 错误,因为 A_get_num 没有this变量
this->m_a++; // 错误
19 return A_g_num;
20 }
21 void A_f(A *this) { A_get_num(); this->m_a++; } // 正确
void A_f2(A *this) {f(this);} // 正确
5. 面向对象实现本质
class A{
public:
static int g_num;
static int get_num() {
return g_num;
}
int f() {
return m_a;
}
private:
int m_a;
};
int A::g_num =1;
int main()
{
A::get_num();
A a;
a.f();
return 0;
}
翻译成 c
struct A{
int m_a;
};
static int A_g_num;
static int A_get_num() {
return A_g_num;
}
int A_f(struct A *this) {
return this->m_a;
}
int main()
{
A_get_num();
struct A a;
A_f(&a);
return 0;
}
0. 友元
友元机制,让用户在无法获得类的 .cpp 文件情况下,通过修改 .h 文件,声明友元,从而获得修改类私有属性的能力。
friend 声明在 class{} 内,与 private public protect 无关。
0.1 友元函数
class A {
friend void func(A &a);
private:
int a;
};
void func(A &a)
{
a.a++;
}
0.2 友元类
class A {
friend class B;
private:
int a;
};
class B {
public:
B(int a) { a.a = a; }
private:
A a;
}
0.2 友元类
1. 运算符重载
1.1 本质
T operator+(T &t1, T &t2)
{
T t3(t1.a + t2.a);
return t3;
}
运算符重载本质是函数,cpp编译器给程序员一个机制,定义运算符相关的函数。
1.2 详细讨论
- 重载 二元运算符
objL op objR
重载为成员函数
objL.operator op (objR);
重载为友元函数
operator op (objL, objR);
示例
// t1 + t2 + t3;
T &operator+(T &t) {
a += t.a;
b += t.b;
return *this;
}
// t1 = t2 = t3;
T &operator=(const T t) {
a = t.a;
b = t.b;
return *this;
}
// ++t1;
T& operator++() {
a++;
b++;
return *this;
}
// t1++
const T operator++(int) {
T t = *this;
a++;
b++;
return t;
}
class T{
friend ostream &operator<<(ostream &out, T &t);
friend istream &operator>>(istream &in, T &t);
....
};
ostream &operator<<(ostream &out, T &t)
{
out << t.a << " " << t.b << endl;
return out;
}
istream &operator>>(istream &in, T &t)
{
in >> t.a >> t.b;
return in;
}
操作符重载练习——mystring
#include <iostream>
#include <assert.h>
#include <string.h>
#include <string>
#include <alloca.h>
#include <stdlib.h>
using namespace std;
#define MYSTR_SIZE 256
class mystring {
friend istream & operator>>(istream &in, mystring &s);
friend ostream & operator<<(ostream &in, mystring &s);
public:
~mystring() {
delete []data;
}
mystring(const char *s = "") {
len = strlen(s);
size = (len / MYSTR_SIZE + 1) * MYSTR_SIZE;
data = new char [size];
strncpy(data, s, len);
data[len] = 0;
}
mystring(mystring &s) {
len = s.len;
size = (len / MYSTR_SIZE + 1) * MYSTR_SIZE;
data = new char [size];
strncpy(data, s.data, len);
data[len] = 0;
}
bool operator<(mystring &s) {
if (len < s.len)
return true;
else if (len > s.len)
return false;
return strcmp(s.data, data) > 0 ? false : true;
}
bool operator>(mystring &s) {
if (len > s.len)
return true;
else if (len < s.len)
return false;
return strcmp(s.data, data) > 0 ? true : false;
}
bool operator==(mystring &s) {
if (s.len != len)
return false;
return strcmp(s.data, data) == 0 ? true : false;
}
bool operator!=(mystring &s) {
if (s.len != len)
return true;
return strcmp(s.data, data) == 0 ? false : true;
}
mystring& operator=(mystring &s) {
if (s.len >= size) {
size = (s.len/MYSTR_SIZE + 1) * MYSTR_SIZE;
delete []data;
data = new char[size];
}
strncpy(data, s.data, s.len);
len = s.len;
data[len] = '\0';
return *this;
}
char &operator[](int i) {
assert(i < len);
return data[i];
}
private:
char *data;
int len;
int size;
};
istream & operator>>(istream &in, mystring &s) {
char c, *p;
if (s.len != 0)
s.len = 0;
while ((c = in.get()) != '\n' ) {
if (s.len >= s.size) {
s.size = s.size + MYSTR_SIZE;
p = new char [s.size];
strncpy(p, s.data, s.len);
delete [] s.data;
s.data = p;
}
s.data[s.len++] = c;
}
s.data[s.len] = '\0';
return in;
}
ostream & operator<<(ostream &out, mystring &s) {
if (s.data == NULL || s.len == 0)
return out;
cout << s.data;
return out;
}
int main()
{
mystring a;
mystring b("abc");
mystring c = b;
cin >> a;
cout << "a : " << a << endl;
cout << "b : " << b << endl;
cout << "c : " << c << endl;
if (a == b)
cout << "a == b" << endl;
if (a != b)
cout << "a != b" << endl;
if (a > b)
cout << "a > b" << endl;
if (a < b)
cout << "a < b" << endl;
a = b;
cout << "a : " << a << endl;
cout << "b : " << b << endl;
a[1] = '1';
a[2] = '2';
cout << a << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?