Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

CPlusPlus友元函数与友元类

发表于 2021-12-21

私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦。

C++ 设计者认为, 如果有的程序员真的非常怕麻烦,就是想在类的成员函数外部直接访问对象的私有成员,那还是做一点妥协以满足他们的愿望为好,这也算是眼前利益和长远利益的折中。因此,C++ 就有了友元(friend)的概念。打个比方,这相当于是说:朋友是值得信任的,所以可以对他们公开一些自己的隐私。

友元分为两种:友元函数和友元类。

友元函数

在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为“友元”,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。

友元类

一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员。

参考链接

  1. C++友元函数和友元类(C++ friend)详解,by c语言中文网.

Win32消息循环及窗口创建注册示例

发表于 2021-12-19

学习Windows应用程序的创建过程对理解OSG的Win32图形上下文有帮助。

Windows应用程序的创建过程

实现窗口创建的六步骤:

  1. 创建一个窗口首先要注册一个窗口类,初始化wndclass中的各个域,设置窗口过程函数。
  2. 调用RigisterClass来注册这个窗口类。
  3. 创建窗口。CreateWindow
  4. 显示窗口。ShowWindow
  5. 刷新窗口。UpdateWindow
  6. 消息循环。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Win32GUIMsg.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "Win32GUIMsg.h"

#define MAX_LOADSTRING 100

// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名

// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

// TODO: 在此处放置代码。

// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_WIN32GUIMSG, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);

// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32GUIMSG));

MSG msg;

// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return (int) msg.wParam;
}



//
// 函数: MyRegisterClass()
//
// 目标: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;

wcex.cbSize = sizeof(WNDCLASSEX);

wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32GUIMSG));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WIN32GUIMSG);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

return RegisterClassExW(&wcex);
}

//
// 函数: InitInstance(HINSTANCE, int)
//
// 目标: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中

HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

if (!hWnd)
{
return FALSE;
}

ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);

return TRUE;
}

//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目标: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;

case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}

参考链接

  1. 消息循环,注册窗口,创建窗口【图解】,by hnfxs.
  2. Windows消息循环理解及窗体创建步骤,by LUCKYONE906.

CPlusPlus父类子类构造函数的初始化过程

发表于 2021-12-18

创建子类对象时,子类构造函数和父类构造函数的调用过程值得好好研究。

参考链接

  1. c++ 子类构造函数初始化及父类构造初始化,by sevencheng798.
  2. C++ 类构造函数 & 析构函数,by runoob.

JavaScript代码混淆方法

发表于 2021-12-17

JavaScript代码混淆就是将JavaScript源代码,通过一系列的转换,例如变量/函数/参数重命名、字符串删除等,转换成不可读的东西,同时还能像以前一样工作。

JavaScript Obfuscator Tool是一个免费且高效的 JavaScript 混淆器(包括对 ES2019 的部分支持)。使您的代码更难以复制并防止人们窃取您的工作。例如:

混淆前:

1
2
3
4
5
// Paste your JavaScript code here
function hi() {
console.log("Hello World!");
}
hi();

使用该工具混淆后:

1
(function(_0x70aa38,_0x6395e8){var _0x57099e=_0x192f,_0x7c1cc7=_0x70aa38();while(!![]){try{var _0x56bcff=-parseInt(_0x57099e(0xf0))/0x1*(-parseInt(_0x57099e(0xee))/0x2)+-parseInt(_0x57099e(0xef))/0x3+parseInt(_0x57099e(0xeb))/0x4+parseInt(_0x57099e(0xf3))/0x5+-parseInt(_0x57099e(0xf2))/0x6+-parseInt(_0x57099e(0xf4))/0x7*(-parseInt(_0x57099e(0xea))/0x8)+parseInt(_0x57099e(0xed))/0x9*(-parseInt(_0x57099e(0xf1))/0xa);if(_0x56bcff===_0x6395e8)break;else _0x7c1cc7['push'](_0x7c1cc7['shift']());}catch(_0x5a9257){_0x7c1cc7['push'](_0x7c1cc7['shift']());}}}(_0x4312,0xd7ab0));function _0x192f(_0x596bfb,_0x95be29){var _0x43126e=_0x4312();return _0x192f=function(_0x192fed,_0x3ed7b3){_0x192fed=_0x192fed-0xe9;var _0x49c785=_0x43126e[_0x192fed];return _0x49c785;},_0x192f(_0x596bfb,_0x95be29);}function hi(){var _0x445057=_0x192f;console[_0x445057(0xec)](_0x445057(0xe9));}function _0x4312(){var _0x4f3249=['Hello\x20World!','9485888XtUGcp','2200444wypUiR','log','99CDZgzo','29822AbjrMy','403245avFbPY','43uwDKOW','2388280NXrOlo','134622vvoFuf','6451580kiRbTs','7mxMQdg'];_0x4312=function(){return _0x4f3249;};return _0x4312();}hi();

打开Chrome浏览器控制台,将混淆后的代码粘贴并运行,会发现结果是一样的。

参考链接

  1. JavaScript Obfuscator Tool,by obfuscator.

Matlab浮点数比较方法

发表于 2021-12-17

Matlab中的浮点数按照IEEE754约定,以有限的精度进行存储。因此,在进行浮点数的比较时,要特别小心,防止看不见的误差导致错误的结果。例如在Matlab中输入如下代码:

1
2
testNum=0:0.1:1
testNum(4) == 0.3 % 结果将表示两者不相等

最好采用如下方式比较两个浮点数是否相等:

1
abs(testNum(4)-0.3)<eps

参考链接

  1. While comparing the equality of two floating numbers(a,b), we use abs(a – b) < tolerance. Could anyone let me know if there’s anyway that this function may yield bad results or errors ?,by mathworks.

CPlusPlus静态变量

发表于 2021-12-14 | 更新于 2024-04-25

static 是 C/C++ 中很常用的修饰符,它被用来控制变量的存储方式和可见性。

我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义为全局的变量,但定义一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅只受此函数控制)。static 关键字则可以很好的解决这个问题。

另外,在 C++ 中,需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。

c++类静态成员函数调用成员变量

方法一

一种最简单的方法就是在类静态函数的形参列表里面加入类指针,到时候直接传入实例的this指针就能操作类成员变量了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A
{
public:
static void test(A *a)
{
a->m_a += 1;
}
void hello()
{
}
private:
static int m_staticA;
int m_a
};

方法二

一开始定义的时候就把实例的this指针复制给一个静态的类指针,这样不用传入this指针直接操作,但是这样如果多个实例都需要操作的时候会出现问题,定义多个变量的时候需要注意静态指针指向的是哪个实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A
{
public:
A()
{

}
static void test()
{
m_gA.m_a += 1;
}
void hello()
{
}
private:
static int m_staticA;
static A *m_gA;
int m_a
};

A* A::m_gA=NULL;

参考链接

  1. C/C++ 中 static 的用法全局变量与局部变量,by runoob.
  2. C/C++ 中的static关键字,by Arkin.
  3. c++类静态成员函数调用成员变量,by xxwtiancai.
  4. C++类里面定义一个指向自己的静态成员函数,by JJ_S.

shotcut视频编辑软件使用帮助

发表于 2021-12-06

最近有个视频剪辑的需求,以前用过会声会影,但感觉它安装包太大了。用了一下爱剪辑,发现该软件确实比较易用,但各种功能限制较多,广告太多,充值的坑太多。最后还是选择了开源的shotcut,基本也能满足简单的视频编辑要求。

参考链接

  1. Shotcut视频剪辑软件使用,by 清风草原.
  2. 开源视频处理工具Shotcut的用法: 剪切、合并、增加背景音乐、添加字幕、 插入视频、图片转视频并加背景音乐、制作电子相册,by lggirls.

CPlusPlus默认参数

发表于 2021-11-21 | 更新于 2021-12-14

函数的默认参数值,即在定义参数的时候同时给它一个初始值。在调用函数的时候,我们可以省略含有默认值的参数。也就是说,如果用户指定了参数值,则使用用户指定的值,否则使用默认参数的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
void Func(int i = 1, float f = 2.0f, double d = 3.0)
{
cout << i << ", " << f << ", " << d << endl ;
}

int main(void)
{
Func() ; // 1, 2, 3
Func(10) ; // 10, 2, 3
Func(10, 20.0f) ; // 10, 20, 3
Func(10, 20.0f, 30.0) ; // 10, 20, 30
return 0 ;
}

参考链接

  1. C++默认参数,by 滴水瓦.

CPlusPlus之Lambda表达式

发表于 2021-11-20

参考链接

  1. C++ 11 Lambda表达式,by 滴水瓦.

CPlusPlus函数设计技巧

发表于 2021-11-15 | 更新于 2021-11-17

在C++中,参数传递的方式是“实虚结合”。

  • 按值传递(pass by value)
  • 地址传递(pass by pointer)
  • 引用传递(pass by reference)

参考链接

  1. C语言函数设计的一般原则和技巧,by C语言中文网.
  2. 设计C++函数传参时如何决定使用指针还是引用?,by zhihu.
  3. C++中函数调用时的三种参数传递方式详解,by CC丶Z.
  4. C++ 函数可以直接返回一个对象吗?,by nettee.
  5. 数组作为函数参数,by LiHongxi.
  6. C++类对象作为函数参数传递详解,by c语言中文网.
  7. C++引用10分钟入门教程,by c语言中文网.
  8. C++中类成员对象与指针的区别,by PengStrongLee.
  9. C++中using的三种用法,by 算法集市.
  10. 10、【C++】前向声明/嵌套类/局部类(内部类),by 阿慕路泽.
  11. C++11 noexcept 关键字用法学习,by CNBLOG.
上一页1…192021…53下一页

Jack Huang

521 日志
67 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse