Skip to content

Monday, 18 March 2024

Since a brief tryst with Ubuntu Budgie Edition, I’ve dearly missed its Raven side-panel, a special panel on the side of the screen that can be opened and closed with a click. As someone who needs a clean, minimal desktop, the workflow is just too perfect — when you have two or three widgets that you use frequently, but not frequently enough that they warrant permanent homes on a main panel, just stuff them into a disappearing side-panel that can be called with a quick key-combination or by clicking on an icon; It’s a great way to keep things out of the way, but within reach, without having a permanently cluttered system tray that you might want to keep clear for things like email notifications.

A cropped screenshot of my plasma desktop showing a side-panel on the right side of the screen containing the clipboard history widget and the media player widget. On the bottom panel is the Scriptinator plugin, showing a tooltip the following title "Show Panel," and body text "Show the hidden right panel.
My side panel, and the widget that launches it.

There are some drawbacks; this workflow isn’t well supported on KDE Plasma, so it’s a bit of a faff to set up, and only a few widgets will display nicely on a wide side-panel. For instance, it would be a dream to have the KDE weather widget automatically take advantage of the horizontal space and display the information that would usually be in its dropdown, but what you get instead is a giant icon, for now at least. I use my side-panel for my clipboard history and the media player widget, both of which play nicely with a side-panel. Another niggle I have with it is that, as far as I know, there’s no way to disable activation of the panel when your mouse pointer makes contact with the screen edge. This is a mild to moderate inconvenience when you’re working with applications that have toolbars on the sides of the window, like design applications often do.

For me, personally, the drawbacks aren’t so severe as to put me off of the workflow.

Creating and configuring the panel #

First, you’ll need to create a panel. To do this, right click on an empty section of your desktop, and select “Add Panel > Empty Panel.” When the panel appears, right click it and select “Enter Edit Mode.” Set up your panel however you like, but you will need to set “Visibility” to “Auto Hide” and may want to give it a width of at least 400px or so.

The panel settings configuration window.

Setting up the script #

Now, if you wanted to show and hide your panel with a keyboard shortcut, you can set up a focus shortcut in the panel settings window and stop here. If, like me, you want to toggle your panel by clicking on an icon somewhere, we’re going to have to use a wee script, but don’t worry, it’s not as hard as it sounds and I’ll take you through it step by step.

Before we can put our script together, we’re going to need to know what the ID of our panel is. Open up KRunner with Alt+F2 or Alt+Space and run plasma-interactiveconsole. This will launch KDE’s Desktop Shell Scripting Console. In the console, type print(panelIds); and click “Execute.” Assuming you entered that in correctly, what you should see now in the output console beneath the text editor is a series of numbers — the ID numbers of our panels. Keep a note of these numbers.

The interactive console showing a series of panel IDs printed to the console.
Look at those IDs.

Clear the text editor and enter the following:

let panel = panelById(401);

panel.hiding === "autohide" ? panel.hiding = "windowsgobelow" : panel.hiding = "autohide";

This will check if our panel is set to auto-hide; if it is, the script will set the panel to “windows go below” mode, otherwise it will set the panel to auto-hide.

Now to make use of those panel ID numbers. Which number corresponds to your new side-panel? While I can’t be sure, chances are it’s the last number on the list as we’ve just made the new panel a moment ago. So in the script above, where I have entered 401, enter the last number in your ID list and click “Execute.” At this point, if the ID number is correct, your panel should appear; click “Execute” once more to hide it.

Setting up the Scriptinator widget #

Alright, we’ve got our script ready, so we just need one more thing in place: a button or icon that we can click on to show and hide the panel. Fortunately, we can use a widget called “Scriptinator” to provide just this. Right click on an empty area of your desktop or a panel, click “Add Widgets,” and “Get New Widgets.”

A cropped screenshot of the widgets panel with the "get new widgets" button at the top.
Let’s get that widget.

From here, find and install Scriptinator. Once installed, simply drag it where you’d like it to live, either on your desktop, or on a panel. Once you’ve done that, right click on the widget and choose “Configure Scriptinator.” Here, enter the path of the icon you’d like to use in “Custom icon full path;” I used /usr/share/icons/breeze-dark/actions/22/sidebar-expand-right-symbolic.svg. In “OnClick Script,” enter the following:

qdbus org.kde.plasmashell /PlasmaShell evaluateScript ''

and between those single-quote marks, paste in the full script we put together in the Desktop Shell Scripting Console, like this:

qdbus org.kde.plasmashell /PlasmaShell evaluateScript 'let panel = panelById(401);

panel.hiding === "autohide" ? panel.hiding = "windowsgobelow" : panel.hiding = "autohide";'
The Scriptinator configuration window.

Set up a tooltip if you like, hit apply, and test out your toggle button.

Success! #

If you’ve done everything correctly, you should see your side-panel appear when you click the widget and disappear when you click a second time. You may need to restart to see your icon applied to the widget; if you don’t want to wait, you can drop the file path into “OnClick icon full path” in your Scriptinator configuration.

Sunday, 17 March 2024

We have a release of Kile 2.9.95, also known as 3.0 beta 4! Earlier today, Michel Ludwig tagged the current Git master. This is the first beta release since October 2019. Beside the port to KDE Frameworks 6 and Qt 6, it provides a couple of new features and bug fixes.

New features

  • Port to KDE Frameworks 6 & Qt 6 (Port by Carl Schwan)
  • Enable high-dpi support
  • Provide option to hide menu bar (Patch by Daniel Fichtner, #372295)
  • Configurable global default setting for the LivePreview engines (Patch by Florian Zumkeller-Quast, #450332)
  • Remove offline copy of "LaTeX2e: An unofficial reference manual", use online version instead (Patch by myself, Christoph Grüninger, Issue #7)

Fixed bugs

  • Kile crashes on selecting "Browse" or "Zoom" for document preview (Patch by Carl Schwan, #465547, #476207, #467435, #452618, #429452)
  • Kile crashes when generating new document (Patch by Carl Schwan, #436837)
  • Ensure \end{env} is inserted in the right place even when the user uses tabs for indentation (Patch by Kishore Gopalakrishnan, #322654)
  • Avoid saving console commands in bash history (Patch by Alessio Bonfiglio, #391537, #453935)
  • Don't crash when deleting templates (#413506)
  • Avoid crashing when closing a document that is being parsed (#404164)

Thanks to all the contributors. They fixed bugs, wrote documentation, modernized the code, and in general took care of Kile.

Enjoy the latest Kile release!

Dear digiKam fans and users,

After four months of active maintenance and long bugs triage, the digiKam team is proud to present version 8.3.0 of its open source digital photo manager.

This version arrives with the internal RAW decoder Libraw updated to the rolling-release snapshot 2024-02-02. Long time bugs present in older versions have been fixed and we spare a lot of time to contact users to validate changes in pre-release to confirm fixes before deploying the program in production.

利用条件表达式完成下面的命题:学习成绩成绩>=90分的同学用A表示,学习成绩在60~89分的同学用B表示哦=,学习成绩在60分以下的同学用C表示.

int gra=0;

class

class 是一种用于创建用户定义的数据类型的关键字。它是面向对象编程(OOP)的基本概念之一。通过使用 class,您可以将数据成员(属性)和成员函数(方法)封装在一个单一的实体中,从而使代码更有组织性和可维护性。

语法: class 类名 {访问权限 : 属性\行为};

class 的功能与struct类似,但提供了更为丰富的功能.

class 可以在设计的时,可以把属性和行为放在不同的权限下,加以控制.

权限的分类

  • public 公共权限
  • protected 保护权限
  • private 私有权限
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
//公共权限  public
//保护权限 protected
//私有权限 private
//私有权限与保护权限在类内可以访问,在类外不可以访问
class Num
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
public:
void func()
{
m_A=10;
m_B=20;
m_C=30;
}
}

int main()
{
Num m;
m.m_A=10;
//m.m_B=20;//类外不可以访问
//m.m_C=30;
}

class与struct的区别

  • struct 默认权限为公共
  • class 默认权限为私有

对象的初始化清理

对象的初始化和清理是两个非常重要的安全问题.

一个对象或者变量没有初始状态,对其使用后果未知

同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题

c++利用了构造函数析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会给我们提供空实现

构造函数

构造函数语法: 类名(){}

  1. 构造函数,没有返回值也不写void
  2. 函数名称与类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在调用对象时候会自动调用构造,无需手动调用,而且只会调用一次
1
2
3
4
5
6
7
8
9
class Num
{
public:
Num(int a): m_A(a)
{
cout << "gz" << endl;
}
int m_A;
};

构造函数的分类:

  • 按参数
    • 有参构造
    • 无参构造
  • 按类型
    • 普通构造
    • 拷贝构造

构造函数的调用方法

  • 括号法

    1
    2
    3
    Person P;//默认构造时后面不要加括号
    Person p1(10);
    Person p2(p1);
  • 显示法

    1
    2
    3
    Person p;
    Person p1=Person(10);
    Person P2=Person(p2);
  • 隐式转换法

    1
    2
    Person p4=10;
    Person p5=p4;//相当于Person p5=Person(10);

explcit

在 C++ 中, explicit 关键字用于防止编译器进行隐式类型转换。当您将构造函数或转换函数声明为 explicit 时,这意味着该构造函数或转换函数只能由程序员显式调用,而不能由编译器隐式调用。

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
class P
{
public:
P()
{

}
P(int a)
{
m_A = a;
}
explicit P(P& a)
{
m_A = a.m_A;
}
int m_A;
};

int main()
{
P p;
p.m_A = 1;
//P p1 = p;//会报错,因为声明不可以隐式类型转换
P p1=P(1);
cout << p1.m_A << endl;
return 0;
}

拷贝构造函数调用时机

c++拷贝构造函数调用时机

  • 使用一个创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值(做参数)
  • 以值的方式返回局部对象(做返回值)

构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数

1.默认构造参数(无参,函数体为空)

2.默认析构函数(无参,函数体为空)

3.默认拷贝构造函数,对属性值进行拷贝

规则如下:

  • 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,c++不会在提供其他的构造函数

深浅拷贝

浅拷贝:简单的赋值操作

深拷贝:在堆区重新申请空间,进行拷贝操作.

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
class Num
{
public:
Num()
{
cout << "gz" << endl;
}
Num(const Num &other)
{
m_A=other.m_A;
m_B=new int (*other.m_B);
cout << "kb" << endl;
}
~Num()
{
if(this->m_B)
{
delete this->m_B;
this->m_B=nullptr;
}
}
int m_A;
int *m_B;
};

int main()
{
Num a;
a.m_A=1;
a.m_B=new int(20);
Num b(a);
cout << b.m_A << endl;
cout << *b.m_B << endl;
cout << a.m_B << endl;
return 0;
}

class 与 struct

C++中的 struct 和 class 基本是通用的,唯有几个细节不同:

  • 使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。
  • class 继承默认是 private 继承,而 struct 继承默认是 public 继承。
  • class 可以使用模板,而 struct 不能。

引用

作用:给变量起别名

语法:数据类型 &别名 =原名

注意事项

  • 引用必须要初始化
  • 引用一旦初始化后,就不可以更改

引用可以简化指针的用法.

引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参

优点:可以简化指针修改实参

通过引用参数产生的效果按地址传递是一样的。引用的语法更加的简单。

引用是可以作为函数的返回值存在的.

不返回局部变量的引用.

函数的调用可以作为左值.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int sum1 = 0;
int& sum(int &a,int &b)
{
sum1 = a + b;
return sum1;
}

int main()
{
int a=10;
int b=10;
sum(a,b)=30;
cout << sum1 << endl;
return 0;
}

本质:引用的本质在c++内部实现是一个指针常量.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void func(int &ref)
{
ref=100;
}

int main()
{
int a=10;
//自动转换为int *const ref = &a;指针常量是指针指向不可改,也说明为什么引用不可以改
int &ref=a;
ref=20;

func(a);
return 0;
}

左值 & 右值

C++中所有的值都必然属于左值、右值二者之一.

左值是指表达式结束之后依然存在的持久化对象

右值是之表达式结束时就不再存在的临时对象.

所有的具有变量或者对象都是左值,而右值不具有名称.很难得到左值和右值的真正定义,但是有一个可以区分的便捷方法:看能否对表达式取地址,如果可以,则为左值,否则为右值。

右值又分为 将亡值纯右值

纯右值是C++98标准中右值的概念,如非引用返回的函数返回的临时变量值;

而将亡值则是c++新增的和右值引用相关的表达式,这样的表达式通常是将要移动的对象。&&函数返回值、std::move()函数的返回值等.

categories来说,c++11前,只有左值(lvalue)和右值(rvalue);c++11后,任何value categories(值类型)都是下面的三种之一:lvaluexvalueprvaluegvalue为广义左值,包括lvaluexvaluervalue为右值,包括xvalueprvalue,可见xvalue可以是左值也可以是右值。

纯右值(prvalue):纯右值是传统右值的一部分,纯右值是表达式产生的中间值,不能取地址。 纯右值一定会在表达式结束后被销毁。

一般有以下的条件:

  1. 本身就是纯粹的字面值,如3、false.
  2. 求值结果相当于字面值或是一个不具名的临时变量.

消亡值(xvalue)是c++11的不具名的右值引用引入的。 以下情况的表达式求值结果为xvalue,准确的说叫不具名右值引用,这种属于新的”右值”,由右值引用带来,通常用来完成移动构造移动赋值的特殊任务。事实上,将亡值不过是C++11提出的一块晦涩的语法糖。它与纯右值在功能上及其相似,如都不能做操作符的左操作数,都可以使用移动构造函数和移动赋值运算符。当一个纯右值来完成移动构造或移动赋值任务时,其实它也具有“将亡”的特点。一般,我们不必刻意区分一个右值到底是纯右值还是将亡值。

左值引用 & 右值引用

c++98中的引用很常见了,就是给变量取了个别名,在c++11中,因为增加了右值引用(rvalue reference)的概念,所以c++98中的引用都称为了左值引用(lvalue reference**)**。 c++11中的右值引用使用的符号是&&

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

A getTemp()
{
return A();
}

int main()
{
int a=10;
int &refA = a;//左值引用
//int &ref2 = 2; //编译错误
int &&ref1=1;//右值引用
int b =5;
//int && refB =b;//编译错误,不能将一个左值赋值给一个右值引用
A&& refIns = getTemp();//函数返回值是 右值
return 0;
}

getTemp()返回的右值本来在表达式语句结束后,其生命也就该终结了(因为是临时变量),而通过右值引用,该右值又重获新生,其生命期将与右值引用类型变量refIns的生命期一样,只要refIns还活着,该右值临时变量将会一直存活下去。实际上就是给那个临时变量取了个名字。

注意:这里refIns类型是右值引用类型(int &&),但是如果从左值和右值的角度区分它,它实际上是个左值。因为可以对它取地址,而且它还有名字,是一个已经命名的右值。

常量左值引用

左值引用只能绑定左值,右值引用只能绑定右值,如果绑定的不对,编译就会失败。但是,常量左值引用却是个奇葩,它可以算是一个“万能”的引用类型,它可以绑定非常量左值、常量左值、右值,而且在绑定右值的时候,常量左值引用还可以像右值引用一样将右值的生命期延长,缺点是,只能读不能改。

1
const A &a =getTmp();//不会报错
1
2
3
4
5
6
7
8
9
10
11
12
int max(const int &a,const int &b)
{
return a+b;
}

int main()
{
int a=1;
int b=2;
cout << max(1,2) << endl;
return 0;
}

总结

T是一个数据类型

  1. 左值引用,使用T&只能绑定左值
  2. 右值引用,使用T&&只能绑定右值
  3. 常量左值,使用const T&,既可以绑定左值也可以绑定右值
  4. 已命名的右值引用,编译器会认为是个左值
  5. 编译器有返回值优化,但不要过于依赖(-fno-elide-constructors)

移动语义

移动构造函数 & 移动赋值函数

用c++实现一个字符串类MiniStringMiniString内部管理一个C语言的char *数组,这个时候一般都需要实现拷贝构造函数和拷贝赋值函数,因为默认的拷贝是浅拷贝,而指针这种资源不能共享。代码如下:

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
class MiniString
{
public:
static size_t kbsize;
MiniString(const char *str)
{
if(str)
{
m_data = new char[strlen(str) + 1];
strcpy(m_data, str);
}
else
{
m_data = new char[1];
*m_data = '\0';
}
}
MiniString(const MiniString &other)
{
kbsize ++;
m_data=new char[strlen(other.m_data) + 1];
strcpy(m_data, other.m_data);
//cout << "kb" << kbsize << endl;
}
MiniString &operator=(const MiniString &other)
{
if(this==&other)
{
return *this;
}
delete[] m_data;
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data, other.m_data);
return *this;
}
~MiniString()
{
if(m_data)
{
delete[] m_data;
}
}
private:
char *m_data;
};

size_t MiniString::kbsize =0;

int main()
{
vector<MiniString> v;
v.reserve(1000);
for(int i=0; i<1000; i++)
{
v.push_back(MiniString("hello world!"));
}
cout << MiniString::kbsize << endl;

return 0;
}

代码执行了1000次拷贝构造函数,如果MiniString("hello")构造出来的字符串本来就很长,构造一遍就很耗时了,最后却还要拷贝一遍,而MiniString("hello")只是临时对象,拷贝完就没什么用了,这就造成了没有意义的资源申请和释放操作,如果能够直接使用临时对象已经申请的资源,既能节省资源,又能节省资源申请和释放的时间。而C++11新增加的移动语义就能够做到这一点。

移动语义避免了移动原始数据,而只是修改了记录。要实现移动语义就必须增加两个函数:移动构造函数和移动赋值构造函数。必须让编译器知道什么时候需要复制,什么时候不需要复制。这就是右值引用发挥最大作用的地方。

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
#include <iostream>
using namespace std;
#include <cstring>
#include <vector>

class MiniString
{
public:
static size_t kbsize;
static size_t movesize;
static size_t Fkbsize;
static size_t Fmovesize;
MiniString(const char *str)
{
if(str)
{
m_data = new char[strlen(str) + 1];
strcpy(m_data, str);
}
else
{
m_data = new char[1];
*m_data = '\0';
}
}
MiniString(const MiniString &other)
{
kbsize ++;
m_data=new char[strlen(other.m_data) + 1];
strcpy(m_data, other.m_data);
// cout << "kb" << kbsize << endl;
}
MiniString(MiniString &&other)
{
m_data=other.m_data;
movesize ++;
other.m_data=NULL;
}
MiniString &operator=(const MiniString &other)
{
Fkbsize ++;
if(this==&other)
{
return *this;
}
delete[] m_data;
m_data = new char[strlen(other.m_data) + 1];
strcpy(m_data, other.m_data);
return *this;
}
MiniString &operator=(MiniString && other)
{
Fmovesize++;
if(this==&other)
{
return *this;
}
delete[] m_data;
m_data=other.m_data;
other.m_data=NULL;
return *this;
}
~MiniString()
{
delete[] m_data;
}
private:
char *m_data;
};

size_t MiniString::kbsize =0;
size_t MiniString::movesize=0;
size_t MiniString::Fkbsize=0;
size_t MiniString::Fmovesize;

int main()
{
vector<MiniString> v;
v.reserve(1000);
for(int i=0; i<1000; i++)
{
v.push_back(MiniString("hello world!"));
}
cout <<"kbsize\t"<< MiniString::kbsize << endl;
cout <<"Fkbsize\t"<< MiniString::Fkbsize << endl;
cout <<"movesize\t"<< MiniString::movesize << endl;
cout <<"Fmovesize\t"<< MiniString::Fmovesize << endl;

return 0;
}

移动构造函数与拷贝构造函数的区别是,拷贝构造的参数是const MiniString& str,是常量左值引用,而移动构造的参数是MiniString&& str,是右值引用,而MiniString("hello")是个临时对象,是个右值,优先进入移动构造函数而不是拷贝构造函数。而移动构造函数与拷贝构造不同,它并不是重新分配一块新的空间,将要拷贝的对象复制过来,而是”偷”了过来,将自己的指针指向别人的资源,然后将别人的指针修改为nullptr,这一步很重要,如果不将别人的指针修改为空,那么临时对象析构的时候就会释放掉这个资源,”偷”也白偷了。

不用奇怪为什么可以抢别人的资源,临时对象的资源不好好利用也是浪费,因为生命周期本来就是很短,在你执行完这个表达式之后,它就毁灭了,充分利用资源,才能很高效。

当类中同时包含拷贝构造函数和移动构造函数时,如果使用临时对象初始化当前类的对象,编译器会优先调用移动构造函数来完成此操作。只有当类中没有合适的移动构造函数时,编译器才会退而求其次,调用拷贝构造函数。

std::move()

对于一个左值,肯定是调用拷贝构造函数了,但是有些左值是局部变量,生命周期也很短,能不能也移动而不是拷贝呢?C++11为了解决这个问题,提供了std::move()方法来将左值转换为右值,从而方便应用移动语义。其实就是告诉编译器,虽然我是一个左值,但是不要对我用拷贝构造函数,而是用移动构造函数。std::move 的源码实现:

1
2
3
4
5
6
7
8
9
// FUNCTION TEMPLATE move
template <class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable
return static_cast<remove_reference_t<_Ty>&& >(_Arg);
}
//_NODISCARD 这是一种标记,用于指示编译器在忽略函数返回值时发出警告。
//constexpr:表示这个函数是在编译时计算的
//noexcept:表示这个函数不会抛出异常
//remove_reference_t 是 C++ 标准库 <type_traits> 中的一个模板元函数,用于移除类型的引用修饰符(& 或 &&)。

static_cast:

用于显示类型转换.在编译时进行类型检查,用于在不同但相关的类型之间检查.

其他类型转换的函数

  • dynamic_cast:
    • 用于在运行时进行安全的向下转型,主要用于处理继承关系中的多态类型
    • 仅在具有虚函数的类层次结构中才有效
    • 如果转型合法,则返回目标类型的指针或引用,否则返回nullptr
  • reinterpret_case
    • 执行底层的二进制位级别的转换
    • 非常强大,但潜在的不安全,因为它可以忽略类型之间的语义差异

可以看到 std::move 是一个模板函数,通过remove_reference_t获得模板参数的原本类型,然后把值转换为该类型的右值。用C++大师 Scott Meyers 的在《Effective Modern C++》中的话说, std::move 是个cast ,not a move.

注意: 使用 move 意味着,把一个左值转换为右值,原先的值不应该继续再使用(承诺即将废弃)。

1
2
3
4
5
6
7
8
9
10
11
MiniString str1("hello"); // 调用构造函数
MiniString str2("world"); // 调用构造函数
MiniString str3(str1); // 调用拷贝构造函数
MiniString str4(std::move(str1)); // 调用移动构造函数
// cout << str1.get_c_str() << endl; // 此时str1的内部指针已经失效了!不要再使用
// 虽然str1中的m_dat已经称为了空,但是str1这个对象还活着,知道出了它的作用域才会析构,
// 而不是move完了立刻析构
MiniString str5;
str5 = str2; // 调用拷贝赋值函数
MiniString str6;
str6 = std::move(str2); // 调用移动赋值函数,str2的内容也失效了,不要再使用

要注意一下几点:

  1. str6 = std::move(str2),虽然将str2的资源给了str6,但是str2并没有立刻析构,只有在str2离开了自己的作用域的时候才会析构,所以,如果继续使用str2m_data变量,可能会发生意想不到的错误。
  2. 如果我们没有提供移动构造函数,只提供了拷贝构造函数,std::move()会失效但是不会发生错误,因为编译器找不到移动构造函数就去寻找拷贝构造函数,也这是拷贝构造函数的参数是const T&常量左值引用的原因!
  3. c++11中的所有容器都实现了move语义,move只是转移了资源的控制权,本质上是将左值强制转化为右值使用,以用于移动拷贝或赋值,避免对含有资源的对象发生无谓的拷贝。move对于拥有如内存、文件句柄等资源的成员的对象有效,如果是一些基本类型,如intchar[10]数组等,如果使用move,仍会发生拷贝(因为没有对应的移动构造函数),所以说move对含有资源的对象说更有意义。

通用引用

当右值引用和模板结合的时候,就复杂了。T&&并不一定表示右值引用,它可能是个左值引用又可能是个右值引用。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class T>
void func(T&&a,T&&b)
{
cout << a+b << endl;
}

int main()
{
int a=10;
int b=20;
func(a,b);
func(200,300);
return 0;
}

如果上面的函数模板表示的是右值引用的话,肯定是不能传递左值的,但是事实却是可以。这里的&&是一个未定义的引用类型,称为universal references,它必须被初始化,它是左值引用还是右值引用却决于它的初始化,如果它被一个左值初始化,它就是一个左值引用;如果被一个右值初始化,它就是一个右值引用。

注意:只有当发生自动类型推断时(如函数模板的类型自动推导,或auto关键字),&&才是一个universal references

1
2
template <typename T>
void func(T&&a);//这里T需要推导,所以&&是universal references
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template <class T>
class Person
{
public:
Person(T age)
{
m_age = age;
}
T m_age;
};

int main()
{
Person<int> p(1);
cout <<p.m_age << endl;
return 0;
}

template<typename T>
class Test {
Test( Test&& rhs ); // Test是一个特定的类型,不需要类型推导,所以&&表示 右值引用
};
1
2
template <typename T>
void func(std::vector<T>&& data);//在往vector添加元素的时候已经将T推断出来,所以是右值引用

所以最终还是要看T被推导成什么类型,如果T被推导成了string,那么T&&就是string&&,是个右值引用,如果T被推导为string&,就会发生类似string& &&的情况,对于这种情况,c++11增加了引用折叠的规则,总结如下:

  1. 所有的右值引用叠加到右值引用上仍然使一个右值引用。
  2. 所有的其他引用类型之间的叠加都将变成左值引用。

完美转发

所谓转发,就是通过一个函数将参数继续转交给另一个函数进行处理,原参数可能是右值,可能是左值,如果还能继续保持参数的原有特征,那么它就是完美的。

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
#include <iostream>
using namespace std;

void process(int &i)
{
cout << "process(int &)" << i << endl;
}

void process(int &&i)
{
cout << "process(int &&)" << i << endl;
}

void myforward(int &&i)
{
cout <<"myforward(int &&)" << i << endl;
//process(i);
process(forward<int>(i));
}

int main()
{
int a =0;
process(a);
process(1);
process(move(a));
myforward(1);
myforward(move(a));
return 0;
}

上面的例子就是不完美转发,而c++中提供了一个std::forward()模板函数解决这个问题。将上面的myforward()函数简单改写一下:

上面修改过后还是不完美转发,myforward()函数能够将右值转发过去,但是并不能够转发左值,解决办法就是借助universal references通用引用类型和std::forward()模板函数共同实现完美转发。例子如下:

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
void RunCode(int &&m) {
cout << "rvalue ref" << endl;
}
void RunCode(int &m) {
cout << "lvalue ref" << endl;
}
void RunCode(const int &&m) {
cout << "const rvalue ref" << endl;
}
void RunCode(const int &m) {
cout << "const lvalue ref" << endl;
}

// 这里利用了universal references,如果写T&,就不支持传入右值,而写T&&,既能支持左值,又能支持右值
template<typename T>
void perfectForward(T && t) {
RunCode(forward<T> (t));
}

template<typename T>
void notPerfectForward(T && t) {
RunCode(t);
}

int main()
{
int a = 0;
int b = 0;
const int c = 0;
const int d = 0;

notPerfectForward(a); // lvalue ref
notPerfectForward(move(b)); // lvalue ref
notPerfectForward(c); // const lvalue ref
notPerfectForward(move(d)); // const lvalue ref

cout << endl;
perfectForward(a); // lvalue ref
perfectForward(move(b)); // rvalue ref
perfectForward(c); // const lvalue ref
perfectForward(move(d)); // const rvalue ref
}

原文链接

Saturday, 16 March 2024

Transitous is a new community-driven routing service. Add your own city and help make it great.

Friday, 15 March 2024

This blog will be used by the Release Team for communally maintained projects which need a release announcement.

KDE Frameworks, Plasma and KDE Gear will remain on kde.org. But individual releases of apps and libraries which get their own releases can be announced here.

Ruqola 2.1.1 is a bugfix release of the Rocket.chat app.

Improvements:

  • Preview url fixed
  • I implement "block actions" (it's necessary in RC 6.6.x when we invite user)
  • Fix OauthAppsUpdateJob support (administration)
  • Fix update view when we translate message (View was not updated in private channel)
  • Show server icon in combobox server
  • Fix show icon for displaying emoji popup menu when we display thread message
  • Fix jitsi support
  • Fix dark mode

URL: https://download.kde.org/stable/ruqola/
Source: ruqola-2.1.1.tar.xz
SHA256: 6f089271ef9f2f576aa31ddc404bdbc8ddd5ddecba3cd9ff829641123bceb0ae
Windows Build: ruqola-2.1.1-windows-cl-msvc2022-x86_64.exe
Signed by: E0A3EB202F8E57528E13E72FD7574483BB57B18D Jonathan Esk-Riddell jr@jriddell.org https://jriddell.org/esk-riddell.gpg

Sunday, 10 March 2024

If you have not been following this blog series, I made a wrapper for Firefox to be able to run different tabs (and more) in different KDE Plasma Activities.

Often a hurdle to using a piece of software is that it is not packaged for Linux distros.

Kudos to Aurélien Couderc (coucouf), who packaged already 0.4.1 for Debian and provided the patch to make it easier to package to different distros.

With 0.4.2 version of Activity-aware Firefox we applied that patch. Other then that, the functionality remains the same as in 0.4.1.

Then I also wrote an AUR package, so Arch, EndeavourOS etc. should be covered now too.

As a consequence, Repology now lists 12 distro packages for Activity-aware Firefox – that is a great start!

But while large, Debian- and Arch-based distros are just a subset of all available FOSS operating systems that KDE Plasma and Firefox run on. If someone were to put it on Open Build Service to cover also RPM-based and other distros, that would be a great boon!

Contributions welcome, as I am reaching the limit of my skills here.

hook out → server migration successful – more on that some other day

Saturday, 9 March 2024

With KDE’s Frameworks 6 being released recently, I’ve been working on getting Tellico to compile with it. It didn’t actually take too much work since I’ve been gradually porting away from any deprecated functions in Qt5.

There’s plenty to do to make sure everything is fully functional and has the correct appearance. But I’m hopeful to have a release soon. At the moment, the master branch compiles with either KF5/Qt5 or KF6/Qt6.

Tellico With KF6

Friday, 8 March 2024

生成个人访问令牌

生成secrets前首先要生成个人访问令牌

创建令牌

  1. 验证您的电子邮件地址(尚未验证)
  2. 在任何界面的右上角,点击个人资料照片,点击设置
  3. 在左边侧边栏中,点击Developer settings(在最下边)
  4. 然后在Personal access tokensTokens中选择Generate new tokens然后选择第二个

选则您要授予此令牌的范围或权限。要使用令牌从命令行访问存储库,请选择repo。如果还需要其他权限请自行勾选。(workflow也进行选择),然后确定即可

最后

将令牌复制到剪切板。出于安全的原因,在您离开该界面后,您将无法再次看到该令牌。

配置github项目的secrets

在github的仓库中打开设置。

在侧边栏中点击Secrets and variables中的Actions

点击New repository secret

名称是随便起得。Secret就是刚才生成的令牌.

详细教程

更改该仓库的写权限

在该仓库侧边栏中选择Actions当中的General,在该页面下的Workflow当中选择第一个并前保存.

详细教程 

设置贪吃蛇

在仓库中的Actions中点击newworkflow

然后点击set up a workflow yourself

新建snake.yml.

复制以下内容.

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
name: generate animation

on:
# run automatically every 24 hours
schedule:
- cron: "0 */24 * * *"

# allows to manually run the job at any time
workflow_dispatch:

# run on every push on the master branch
push:
branches:
- main



jobs:
generate:
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
# generates a snake game from a github user (<github_user_name>) contributions graph, output a svg animation at <svg_out_path>
- name: generate github-contribution-grid-snake.svg
uses: Platane/snk/svg-only@v3
with:
github_user_name: ${{ github.repository_owner }}
outputs: |
dist/github-contribution-grid-snake.svg
dist/github-contribution-grid-snake-dark.svg?palette=github-dark
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


# push the content of <build_dir> to a branch
# the content will be available at https://raw.githubusercontent.com/<github_user>/<repository>/<target_branch>/<file> , or as github page
- name: push github-contribution-grid-snake.svg to the output branch
uses: crazy-max/ghaction-github-pages@v3.1.0
with:
target_branch: output
build_dir: dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


然后保存.

然后点击Run workflow即可

然后回到该仓库的主页,会生成output分支,里面会有对应提交记录的贪吃蛇的svg图片

然后点击生成的文件,在右上角有Raw,即可查看文件地址.

贪吃蛇详细教程

个人readme文件

将贪吃蛇部分代码部分代码的链接进行替换。