@45°夹角的考研力

1.只出现一次的数字

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int result = nums[0];
        for(int i = 1; i < nums.size() ;i++){
            result = result ^ nums[i];
        }
        return result;
    }
};

2.多数元素

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        //出现次数超过一半的数字一定是出现最多的数字
        map<int , int>mp;
        int max = 0;//出现次数
        int result = 0;//出现次数最多的数字
        for(int i = 0; i < nums.size() ; i++){
            if(mp.find(nums[i]) != mp.end()){//map中找得到该数字
                mp[nums[i]] = ++mp[nums[i]];
                if(mp[nums[i]] > max){
                    max = mp[nums[i]];
                    result = nums[i];
                }
            }
            else{//map中找不到该数字
                mp[nums[i]] = 1;
                if(1 > max){
                    max = 1;
                    result = nums[i];
                }
            }
        }
        return result;
    }
};

3.搜索二维矩阵 II

class Solution {
public:
    bool findTarget(vector<vector<int>>& matrix , int i , int j , int target){
        if(matrix[i][j] == INT_MIN){//递归可能会超时,需要将递归经过的地方设置为INT_MIN,之后递归再次遇到该值直接跳过;
            return false;
        }
        else if(matrix[i][j] == target){
            return true;
        }
        else if(matrix[i][j] > target){
            return false;
        }
        else{
            matrix[i][j] = INT_MIN;
        }

        if(i == matrix.size()-1 && j == matrix[i].size()-1){//最后一行最后一列
            return false;
        }
        else if(i == matrix.size()-1){//最后一行
            return findTarget(matrix , i , j+1 , target);
        }
        else if(j == matrix[i].size()-1){//最后一列
            return findTarget(matrix , i+1 , j , target);
        }
        if(matrix[i][j] < target && matrix[i+1][j] > target && matrix[i][j+1] > target){
            return false;
        }
        return findTarget(matrix , i+1 , j , target) || findTarget(matrix , i , j+1 , target);
    }

    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        return findTarget(matrix , 0 , 0 , target);    
    }
};

4.合并两个有序数组

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        vector<int> nums3;
        int p1 = 0;
        int p2 = 0;
        while(p1 < m && p2 < n){
            if(nums1[p1] <= nums2[p2]){
                nums3.push_back(nums1[p1]);
                p1++;
            }
            else{
                nums3.push_back(nums2[p2]);
                p2++;
            }
        }
        if(p1 == m){
            for(int i = p2 ; i < n ; i++){
                nums3.push_back(nums2[i]);
            }
        }
        else if(p2 == n){
            for(int i = p1 ; i < m ; i++){
                nums3.push_back(nums1[i]);
            }
        }
        nums1.resize(0);
        for(int i = 0; i < m+n ;i++){
            nums1.push_back(nums3[i]);
        }
    }
};

5.验证回文串

class Solution {
public:
    bool isPalindrome(string s) {
        //大写转换为小写
        for(int i = 0; i < s.length() ;i++){
            if(s[i] >= 'A' && s[i] <= 'Z'){
                s[i] = s[i] + 32;
            }
        }
        int i = 0;
        int j = s.length() - 1;
        while(i < j){
            //筛选掉非数字和字母的部分
            while(!((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= '0' && s[i] <= '9'))){
                i++;
                if(i >= j){
                    return true;
                }
            }
            while(!((s[j] >= 'a' && s[j] <= 'z') || (s[j] >= '0' && s[j] <= '9'))){
                j--;
                if(i >= j){
                    return true;
                }
            }
            if(s[i] != s[j]){
                return false;
            }
            else{
                i++;
                j--;
            }
        }
        return true;
    }
};

6.有效的字母异位词

class Solution {
public:
    bool isAnagram(string s, string t) {
        int flag[26];
        memset(flag , 0 , 26*sizeof(int));
        for(int i = 0; i < s.length() ; i++){
            flag[s[i]-'a']++;
        }
        for(int i = 0; i < t.length(); i++){
            flag[t[i]-'a']--;
        }
        for(int i = 0; i < 26 ;i++){
            if(flag[i] != 0){
                return false;
            }
        }
        return true;
    }
};

7.字符串中的第一个唯一字符

class Solution {
public:
    int firstUniqChar(string s) {
        int flag[26];
        memset(flag , 0 , 26*sizeof(int));
        for(int i = 0; i < s.length() ; i++){
            flag[s[i]-'a']++;
        }
        for(int i = 0; i < s.length() ; i++){
            if(flag[s[i]-'a'] == 1){
                return i;
            }
        }
        return -1;
    }
};

8.反转字符串

class Solution {
public:
    void swap(char &s1 , char &s2){
        char tmp = s1;
        s1 = s2;
        s2 = tmp;
    }

    void reverseString(vector<char>& s) {
        int i = 0;
        int j = s.size() - 1;
        while(i < j){
            swap(s[i] , s[j]);
            i++;
            j--;
        }
    }
};

学理工科的,受现代科技高度分化与深入细节的特性所困,往往满脑子被公式,理论,模型,算法,代码……等塞满,推崇理性,但往往并不感到多少幸福,因为理性并不必然带来幸福,更有可能带来中性的“冷静”,或者偏副面的“冷酷”。

人其实还是偏感性的生物,幸福更是一种主观感受而非客观事物,更多来自于关系和社会,科技带不来这些。

但理工科的许多人,大部分时间都被科技所占用,社交圈子不大,生活方式比较单调,真没多少时间去“感悟”和“体会”人生与社会万象。

无法行万里路,见百般人,但可以读万卷书。

我觉得吧,应该多看书,种类要多,还可以看一些优秀的影视文学作品,尤其是那些反映现实而不是构建幻境的优秀作品,还可以多听多看一些智者及过来人的观点和感悟。

在这信息丰富的时代,找到这些其实并不困难。

想办法拥有一个通透的心境,其实比许多外在的东西,比如名利之类,价值更高,更容易让人感到平和与幸福。

每个人的生活,其实就是在讲一个故事。

再理性再智慧的人,也是没有办法完全获知所有真相的,人们是在用自己的方式模拟现实。现实中的一切,本身是没有意义的。魔方是同个颜色都在同一面,还是混乱的颜色,对魔方本身来说有什么区别吗?是人赋予他们意义。最后,现实往往是松散的,事与事之间,关联性很弱。是人把他们串在了一起。形成了我们头脑中的意义,一个连续的故事。

有的故事有趣,有的故事无聊。比如,有人说,人生的意义就在于繁衍后代,延续基因和宗族。那么,这个故事就是千方百计的生育,然后苟活下去。这个就比较无聊了。有的故事就像是打怪探险,遇到一个问题解决一个问题,打怪升级,积累经验,百折不挠,一步步通关。这个故事有趣一些。

有些故事讲着讲着讲崩了。这个时候就得跳出来。不要试图挽回,崩了的故事可能是个漩涡,永远圆不回来。不如存档退出再来。退出的办法有很多,最普遍的就是找个朋友倒苦水。只要他不是什么学了半吊子心理学的,只要是个普通人,给出的建议往往都会中肯有用。

我坐在书桌前,盯着那片白墙看了很久。
其实不算是白墙,墙上贴了浅色纹理的壁纸,只不过在昏暗的灯光下和白墙没什么差别。
在那之前,楼上装修的噪音又惹起了好久不犯偏头痛,我把自己整个蜷缩在沙发上,浑身乏力,头晕目眩,生不如死。
“庆历四年春,滕子京谪守巴陵郡。越明年,政通人和,百废俱兴,乃重修岳阳楼,增其旧制。”
记忆卡在这里,像是戛然而止的磁带,发出细微的嗞啦声,而后归于死寂。
看那面墙看久了,我突然好想推开椅子从窗户那里跳下去。
我希望我死了之后,没有人悲伤,没有人想念我,最好没有一个人记得我。我什么东西都没有拥有过,也不会拥有任何东西。
坐在车上,我只能想到车祸。看见高楼,我只能想到有个人在最高处坠落的样子。看见湖水,我只能想到自己失足掉下去的场景。
但我没有死,我还活着。卑微地、苟且地、懦弱地活着,双目失神地坐在这里,像平常的每一天一样。
并且我知道自己应该活下去。
这个念头让我反胃,于是我只好拼了命地将种种恶心与难受忍回去。好憋屈啊,好难受啊。
我的脑子里很空,什么都没有。它们都流失掉了,或许有一天我会忘记自己叫什么,为什么坐在这里。那时候我只能一遍又一遍地重复着《岳阳楼记》开头的那几句话,或许甚至不知道它们的文字排列有什么意义。
我不知道这样的我能等来什么。终于下定决心的死亡?佝偻苟且掉的一生?还是精神病房的消毒水味?或者是我最无法想象到的一点点救赎。
不可能。写到这里我笑了。
我盯着那面墙,觉得可笑。它像我一样,用力地雕琢了那么久,在昏暗的吊灯和惨白的台灯照射下,还是像一张白纸一样,一无所有。
我这一生,尽是可耻之事。
如果哪天我死了,就把这篇文章打下来,放在我身边吧。

你生活最艰难的时候,
不是物质上的身无分文,
也不是身体上遭受的痛苦折磨,
而是精神上的孤立无援。

无论这种孤立无援是如何产生的,
可能是一件件小事导致的情绪叠加,
亦或是突遭飞天横祸的无能为力。
你崩溃了。
那种感受犹如山洪的爆发,
你拼命的誓死抵抗,
但那种压抑的情绪浩浩荡荡,
螳臂当车,
你做的一切努力都被无情的碾过。
顿时,
你的胸口犹如钢枪穿过,
你的大脑好似暴风卷过。
震颤,压抑,悲愤,无力,失落。
无法冷静,无法克制,无法辩驳。
就连说话的力气都会颤抖到微弱。

你不甘心就此没落,
你被那种情绪一直侵袭着,
一天,两天,一个月,一年……
你始终没有走出来过。

你在同学,同事,朋友,家人面前依然洒脱,
只有你自己明白心里情绪的震烁。
你带着面具,
人前你笑着从未哭过,
只有你自己知道无数个深夜怎么度过。

你尝试着买醉过,
你尝试着得过且过,
你尝试了那么多,
终究还是难以抵挡内心的难过。
熬着熬着你习惯了,
混着混着你明白了,
没有人能拯救你自己,
你感觉迷茫,感觉这辈子就这样了。
可你始终没有想过,
无数个早晨你醒来照的镜子里那个人,
就是你唯一的依靠和解脱。

终于明白,
这个世界陪伴自己最久的终归是自己。
无论再坚固的朋友,
无论再亲密的爱情,
无论再和谐的家庭,
只有自己才是自己最好的寄托。
你要学会和自己好好相处,
你要学会倾听自己内心的声音,
有可能是真,有可能是假,
无论真假,你都要记得好好对待自己。
没有任何人比自己更重要。
这一点不自私。

最后你会发现,
原来那么多的过不去,
其实都是在和自己过不去。
你终于学会了如何和自己相处,
终于放过了自己,
也终于得到了解脱。

我如何知道这么多?
因为我正在经历着,
但还没有经历过。

From 2021.02.21

从前,有一只小鹿,一只小熊,和一只小兔子。
他们度过了很多春夏秋冬。
他们生活在一个生长着七彩树的森林里。
他们看过了不同的阳光穿透不同颜色的叶子。
他们在很多不同地方睡觉。
他们有时候逃亡,有时候安定。
他们互相分享共同成长。
后来小鹿爱上了另一只小鹿,
他走了。
接着小熊又爱上了另一只小熊,
他也走了。
小兔子没有爱上其他的兔子,
但他搬家了。
搬到没有七彩树,没有璀璨阳光的山谷里。
时间缓慢流淌,
像曾经他们屋前的小溪。
小鹿有一次路过,
看到了破败的屋子。
小熊有一次在周围找蜜顺便来看看,
看到了一片青苔满布的废墟。
小兔子一生也没回来过,
那房子还是那样,
再后来,
一只听说了他们故事的鸟儿,
要来这里看看,
他站在溪边望去
他不知道该往哪里看,到处都是花,到处都是树,到处都是蘑菇,到处都是小草。
故事,
是发生在这里吗?
小鸟在这里失声痛哭。

From 2020.12.14

虽然我今年在股票上赚取了十几万元,对于一些人来说,不多,但是对于我,是一种激励与证明,回想起之前失败的亏损时光,那是一段宝贵的经验。
没有人的股票投资生涯是一帆风顺的,你总是要付出一些学费,在跌倒的路上自己默默的爬起来,然后重新整理出发。
这一路真的很寂寞,学习宏观/微观经济学,会被人嘲笑,你学了有什么用?然后尝试学习行为经济学、商业模式、财报、各种投资书籍;还是被人嘲笑,学了用不上,有什么用?但是我不管别人质疑的眼光,坚定的持续学习。
经历过亏损甚至一度吃不上饭还不起债的人有一个好,那就是生死看淡,心态平稳。股价短期的涨跌,只是股民们的情绪波动,与企业价值无关;好的美玉,掉入垃圾桶,它还是美玉;一块普通的石头,哪怕吹的价值上天,它总归是一块石头而已;反而更应该关注那些有价值投资的公司。
市场上,真正能够长期成长的股票,真的不多。因此,可以说,大部分股票都是博弈的游戏,不值得长期持有。
但是,历史数据也告诉我们,能利用市场情绪在股市里长期赚到钱的人,也极其罕见。
相反,因为贪小便宜,想利用市场波动短线走位,结果却错失了大机会的投资者,却十分常见。所以,炒股要挣钱,别贪小便宜。
我们的身边,都可以看见这样一些投资者:这类投资者每次市场下跌,就在市场扫货,目标只是赚一波快钱。在赚取了蝇头小利或者市场陷入横盘后,便迅速卖出。
这类投资者,看似聪明,收益其实未必就好。
一只股票出现调整,如果能在低位买到,两三天之间就能赚个10%。这种跌出来的黄金坑,每只股票一年都会有好几次。
每次10%,当然非常好。可是,在跌市里,相信这些投资者也不会仓位太重,一般只会少量持仓。这样,总的回报就要打折,一年能有超10%的回报,可能就已经是非常罕见了。
如果与长期持有一只好股票,这样的收益就显得有些寒碜了。比如投资贵州茅台这样的公司,只要拿着,放弃短期差价,这样的投资者早已是赚的盆满钵满。
与长期持股相比,热衷短线的投资者不知道需要多少次大跌和黄金坑,多少次的精彩走位,才能炒出这样的回报?!
最后,股票投资,第一心态很重要;第二需要不断的学习金融、经济、投资、历史相关的东西;第三注意身体健康,不然赚钱了,钱都给医院或者没有命花啦。

这个话题并不是一个新的话题,网上有前辈做过详细的描述(Mixing Objective-C, C++ and Objective-C++: an Updated Summary)。之所以做为一篇文章,是因为在实际项目中用到了这种混合,加以记录。

首先需要澄清的是,Objective-C是strict superset of C。C++是基于C语言的面向对象的扩充,但不是strict superset of C。Objective-C++是Objective-C的扩展,使得Objective-C可以链接C++代码。通常情况下,要使用Objective-C++,需要将.m源文件改成.mm源文件。

其次需要澄清的是,Objective-C代码跟C++代码是不能混合使用的,也就是说,Objective-C的头文件中是看不到C++的头文件的。当然,C++的头文件是看不到Objective-C的头文件的。只有Objective-C++文件(也就是.mm文件)可以看到Objective-C的头文件和C++的头文件。

正因为Objective-C和C++互相不能见到对方的头文件,那么就意味着,现有的C++的代码库,想要在Objective-C中使用,是需要封装的,通常,这种封装是借助于Objective-C++写成的Wrapper.

另一方面,Wrapper本身也有技巧,那就是使用Objective-C的class extension特性(或者使用Pointer to implementation设计模式),避免将C++头文件暴露给Objective-C(否则无法编译通过)。具体细节可以参考文章开头的链接。

背景

在使用Qt给MacOS上的截图工具实现自动选中窗口区域需求的时候,原理无非就是获得桌面上可见的所有窗口的坐标以及尺寸,再将这些数据用一个结构体封装起来,按照在桌面的Z序进行排序,最后对鼠标的移动事件进行判断,按照Z序去对鼠标是否在某个窗口的矩形范围内进行判断,直到找到在范围内的矩形,这就是我们要自动选中的矩形了,如果将所有窗口都遍历完了还没发现鼠标正在某个矩形内的话,就不进行自动选中操作。

在Windows下我们可以很方便的使用EnumWindows这个Win32 API去获得桌面上所有窗口的坐标和尺寸,Win32 API可以由C++很方便的调用,而在MacOS上,单纯的使用C++仿佛无法获得我需要的这些数据,于是只好使用Apple的Objective-C提供的接口去获得这些数据。

通过在苹果开发者文档找到了MacOS上很多获得窗口信息的接口,使用CGWindowListCopyWindowInfo就能得到每个窗口的具体数据,接下来我们就要开始Qt与Objective-C混合编程了。

Objective-C是一种在C的基础上加入面向对象特性扩充而成的编程语言,通常称为jObC和较少用的 Objective C或ObjC。在一定程度上,可以把 Objective-C看成是ANSI版本C语言的一个超集,它支持相同的C语言基本语法,同时它还扩展了标准的 ANSI C语言的语法,包括定义类、方法和属性。当然还有其他一些结构的完善和拓展,如类别(Category)的出现。

所以Objective-c是可以直接调用C语言的,那么能否直接调用C++呢?答案是肯定的。

Objective-C源文件介绍

首先我要说一下Objective-C的源文件,后缀是.m或.mm,在.mm文件里,可以直接使用C++代码。所以,我们要混合Qt代码与Objective-C代码,就需要在Qt项目里加入mm文件。

而要混合Objective-C代码,需要更改一下pro文件,添加mm文件,如果有用到MacOS的API的话,则可能还需要添加MacOS的Framework。

添加源文件,需要在.pro中使用OBJECTIVE_SOURCES这个变量,如下所示:

OBJECTIVE_SOURCE += \
Getallvisiblewndpos.mm

添加头文件,需要在.pro中使用OBJECTIVE_HEADERS这个变量,如下所示:

OBJECTIVE_HEADERS += \
getallvisiblewndpos.h

添加Framework,需要在.pro中使用LIBS这个变量,如下所示:

LIBS += -framework CoreGraphics
LIBS += -framework CoreFoundation

混合代码

要使用MacOS提供的框架,需要在.mm文件内包含相关的头文件,又有几部分工作要做。一个是在.pro文件里加入Framework路径,使用LIBS变量即可,不多说了。另外一点是在mm文件内包含Objective-C的头文件,与C++头文件一个理儿,不过使用Objective-C的头文件要使用#import,而使用C++的头文件要使用#include,且所有的#include要写在#import的上面。

需要注意的是,凡是出现了Objective-C源代码和头文件的文件都需要将拓展名改成.mm,只有这样编译器在编译的时候才会既认识Objective-C代码,又认识C++代码。

注意事项

当遇到链接失败的问题时,如下图所示:

通常都是因为只引入了头文件而没有引入对应的Framework,通过在苹果开发者文档或者在Xcode中查找对应的Framework并添加到.pro文件中加以解决。

附赠Qt下Objective-C获得MacOS下所有窗口坐标、尺寸、Z序的方式:

//getAllVisibleWndPos.h
#ifndef GETALLVISIBLEWNDPOS_H
#define GETALLVISIBLEWNDPOS_H

#include <QRect>
#include <QList>
#include <vector>

#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import <CoreGraphics/CGWindow.h>
#import <CoreFoundation/CFArray.h>

struct WND_INFO{
	int layer;//Z序
	int index;//序号
	QRect pos;
};

bool CompareWNDINFO(const WND_INFO& A , const WND_INFO& B);
QList<WND_INFO> GetAllVisibleWndPos();

#endif // GETALLVISIBLEWNDPOS_H
//getAllVisibleWndPos.mm
#import <getallvisiblewndpos.h>

bool CompareWNDINFO(const WND_INFO& A , const WND_INFO& B)
{
	if(A.layer == B.layer)
	{
		return A.index < B.index;
	}
	if(A.layer > B.layer && B.layer > 0)
	{
		return true;
	}
	if(A.layer < B.layer && A.layer > 0)
	{
		return false;
	}
	return A.layer == 0;
}

QList<WND_INFO> GetAllVisibleWndPos()
{
	std::vector<WND_INFO> vec;
	CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements , kCGNullWindowID);
	CFIndex cnt = CFArrayGetCount(windowList);
	for (CFIndex i = 0; i < cnt ; i++)
	{
		NSDictionary *dict = (NSDictionary* )CFArrayGetValueAtIndex(windowList , i);
		int layer = 0;
		CFNumberRef numberRef = (__bridge CFNumberRef) dict[@"kCGWindowLayer"];
		CFNumberGetValue(numberRef , kCFNumberSInt32Type , &layer);
		if(layer < 0)
			continue;
		CGRect windowRect;
		CGRectMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)(dict[@"kCGWindowBounds"]) , &windowRect);
		
		QRectF pos;
		pos.setRect(windowRect.origin.x , windowRect.origin.y , windowRect.size.width , windowRect.size,height);
		
		WND_INFO info;
		info.layer = layer;
		info.pos.setRect(pos.x() , pos.y() , pos.width() , pos.height());
		info.index = i;
		
		vec.push_back(info);
	}
	
	std::sort(vec.begin() , vec.end() , CompareWNDINFO);
	QList<WND_INFO> list;
	for(int i = 0 ; i < vec.size() ; i++)
	{
		list.push_back(vec[i]);
	}
	return list;
}

Windows下打包Qt应用程序

在Qt Creator下用Release编译一遍程序,生成相应的exe文件。

 在Qt Creator下编译好的Release下的exe 拷贝到一个新建的文件夹下面;在开始菜单搜索Qt,找到如图这个命令行程序(命令行程序的版本必须与你使用的编译器版本严格对应,否则打包出来的程序将无法正常运行),然后运行。

打开QT命令行,然后利用cd语句进入你拷贝的exe文件放在的文件夹下; 如:”cd C:\Users\Final\Desktop\XXX”,接着运行命令行语句:”windeployqt XXX.exe”(XXX.exe用你的exe文件名替代),然后文件夹下就会出现所有QT依赖。

然后将这个文件夹打包好就可以发布了。如果需要将所有的dll以及其他资源文件全部打包成一个exe,我们可以使用Engima Virtual Box这款软件进行打包。

MacOS下打包Qt应用程序

在Qt Creator下用Release编译一遍程序,生成相应的应用程序。

然后找到Qt安装目录下的macdeployqt,比如在我的安装目录下该文件在“/users/dxf/Qt5.9.0/clang_64/bin/”目录,其中Qt5.9.0以及clang_64分别代表Qt的版本和Qt使用的C++编译器,在使用macdeployqt的时候Qt版本以及使用的编译器必须和编译时使用的Qt库版本及编译器严格对应,否则程序运行阶段将会出现不可预知的问题。

将macdeployqt拖入终端中,然后将你的应用程序也拖入终端中,如图所示,执行相应的打包命令,我们的Qt应用程序的所有依赖的库就会被打入应用程序中了。

如果需要打包成MacOS下的dmg文件,只需在命令行后加上-dmg即可,如图所示。

在Windows下对打包的应用程序进行压缩

在对Qt应用程序进行打包之后,这时候就会看到Qt已经把需要用到的DLL都复制过来了。如果觉得这时候整个程序比较大的话,可以根据需要去掉一些东东:

  • libEGL.dll,libGLESV2.dll这两个文件是ANGLE的文件,可以去掉。opengl32sw.dll是软件模拟OpenGL使用到的,除非用户的系统连DirectX支持都不完整——虚拟机环境就是这样——不然这个文件也完全没有用。 QtWidgets/C++程序都不用OpenGL,所以直接去掉即可。可在调用 windeployqt.exe时加”–no-angle”和”–no-opengl-sw”这两个参数。
  • 如果没有使用svg的话,iconenginesqsvgicon.dll, imageformatsqsvg.dll,Qt5Svg.dll这三个文件也可以删掉。
  • 如果没有对应用程序做国际化处理的话,translations里面的翻译文件也可以删掉。
  • QML程序没有使用QtWidgets/C++,可以删掉Qt5Widgets.dll。
  • 如果imageformats目录里面有几种图像格式没用上,也可以删掉。我自己通常把整个目录都删掉,Qt已经编译了png的支持,能读写程序包含的图标就够,其它格式不重要。
  • qmltooling和Qt5Network.dll是用于QML调试用的,可以删掉。

经过以上裁剪,使用7zip压缩完以后,一个QtWidgets/C++的HelloWorld程序最终只剩下5.64M,一个 QtControls程序是5.86M, QtQuick程序还会更小一些。

通常情况下,只需要platforms\qwindows.dll、Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll四个。Qt 5拆得那么散就是想解决这个问题。所以不要打包所有的dll,用到哪个就加哪个。

关于Qt版本对应用程序大小的影响

如果你用的Qt版本为Qt 5.x甚至Qt6.x,5.x和6.x增加了很多新特性的确大了一些。
如果只是写个小程序,不需要5.x的新特性,那么推荐Qt 4.7.x/4.8.x。

Qt适合三种场景:

  • 必须跨平台项目
  • 大项目,代码行数在30万+
  • Qt铁杆粉的项目

这三种场景下,安装包大小都不是主要问题。

减小安装包的方法也很简单:自己编译Qt库。
在configure的时候去掉RTTI,异常,Qt3支持,优化选项用最小大小(性能差不了太多)。
如果你的程序真的很小,那么可以直接静态链接,这样就更小了。

MacOS下应用程序“XXX”不能打开

当MacOS下的应用程序在传输过程中有遇到过压缩的话,经常会遇到「应用程序“XXX”不能打开」的提示,这是由于应用经过压缩和解压之后,其中的可执行文件的权限被抹除,导致无法打开。下面以ScreenShot应用为例子,解决一下这个问题。

1、右击该应用,选择“显示包内容”。

2、依次打开“Content”、“MacOS”目录,找到ScreenShot文件,我们会发现这个文件变成了文本编辑文稿类型的文件,而实际上这个文件应该为Unix可执行文件,由于在压缩的过程中权限丢失,所以这个文件变成了类型不明的文件,导致应用无法打开。

3、打开终端,输入“chmod +x”,注意“chmod”和“+x”中间有个空格,然后再输入空格,将ScreenShot文件拖入终端,接着按回车键执行指令。“chmod +x ScreenShot”的意思就是给ScreenShot这个文件执行权限的意思。

4、执行完这条命令之后,ScreenShot就被成功给予了执行权限,变成了Unix执行文件,这时这个应用就可以正常运行了。

From 2020.11.27

在资本家眼里,你只不过是没有价值的生瓜蛋子。在乎你的感受?不好意思,没时间修补你的玻璃心。你又没什么价值,不想干滚蛋,有的是人干,又不是缺你不可。
我很佩服那些一身本领,既能为公司创造价值又能实现自我价值,毅然决然裸辞潇洒走一回的人,他们或是休息调整找到更好的工作,或是旅行读书充电创业,不断提升自己。
怕被人知道自己过得不好,那么你必须一个人承受,一个人挣扎,一个人做决定。现实却是前怕狼后怕虎,畏手畏脚,逃避现实,不敢开始,害怕失败。
人一闲下来就废了。