分类 笔记 下的文章

C++ 几乎可以重载全部的运算符,而且只能够重载C++中已经有的。

· 不能重载的运算符:“.”、“.*”、“::”、“?:” · 重载之后运算符的优先级和结合性都不会改变。

可以重载为类的非静态成员函数; 可以重载为非成员函数。

重载单目运算符,前置的单目运算符不需要提供形参。如 ++ -- *= +=...

而后置的单目运算符是需要提供参数来区别前置(为了重载)的。

class Even{
    int number=0;
    public:
    A & operator ++ (){
        number +=2;
        return *this;
    }
    A operator ++ ( int ){
        int old = number;
        ++(number);
        return old;
    }
}

前置++ 返回的是左值,而后置++ 返回的只是一个右值。

重载双目运算符,需要提供一个形参。如 + - * % /...

class Matrix{
    int ** elements;
    int sizeX;
    int sizeY;
    public:
    Matrix & operator + ( const Matrix & m ) const{
        int newX = m.getX() > this.sizeX ? m.getX() : this.sizeX;
        int newY = m.getY() > this.sizeY ? m.getY() : this.sizeY;
        Matrix _new(newX,newY);
        for( int i = 0; i< newX; i++ ){
            for( int j =0; j< newY; j++ ){
                _new[i][j] = m[i][j] + elements[i][j];
            }
        }
        return _new;
    }
}

重载为非成员函数

当需要对当前程序没有权限的类型进行操作符重载的时候,或是将不同类型重载到一起运算,都需要进行非成员函数重载。

重载时需要从左至右依次声明参与预算的各个参数

这个时候可以理解为以重载的形式写的常规函数。

非成员函数的重载操作符参数,不能全为普通类型。

构造函数

c++在进行实例化的时候通常需要使用构造函数,没有显示构造函数的时候,系统会默认一个所有参数为空的默认构造函数。

C++中的构造函数有很多细节,其中从语法上来说,定义在函数声明的部分,是会优先于构造函数本身执行。 譬如说以下的两种方式,会有不同的效果。

class A{
    int X;int Y;
    public:
    A( int x, int y ){
        std::cout << X << std::endl;
        X = x; Y = y;
    }
}
class B{
    int X;int Y;
    public:
    B( int x, int y ): X(x),Y(y){
        std::cout << X << std::endl;
    }
}

A,B都能分别完成对象的构造,区别在于B由于是在声明阶段定义了两个形式参数将要被放置到的对象属性中,所以A的构造函数不能在函数体内的第一行输出我们期望的值。而B中,X属性已经完成了初始化,可以顺利的输出我们的期望值。 另外由于省略了建立、销毁局部参数的过程,这种声明式的构造函数效率更好。

派生类中的构造函数

在派生类中使用构造函数时,需要同时构造基类的构造函数,如果同时继承多个基类,则需要依次构造基类。 在没有进行基类构造的时候,c++会默认使用基类的默认构造函数进行构造,但如果不满足这样的条件,就会报错。

class A{
    int a;
    public:
    A( int a ):a(a){}
}
class B{
    char b;
    public:
    B( char b ):b(b){}
}

class C : public A, public B{
    bool c;
    C( int a, char b, bool c ):A(a),B(b),c(c){}
}

这是一个最基本的多继承构造函数的形式。

有些时候我们可能会需要一些变种构造函数,也就是重载。譬如说当我们基于Matrix设计一个九宫格类的时候,实际上matrix的行和列都是固定的3x3.我们并不需要这两个参数来初始化。 这样的话,我们就可以使用单参数的形式重载九宫格类的构造函数:

template <typename T>
class sMatrix : public Matrix<T>{
private:
    int _sign;
public:
    sMatrix( int sign ): Matrix<T>(3,3), _sign(sign){ cout<< _sign << endl; }
    sMatrix( int x, int y, int s ):Matrix<T>( x, y ){
        cout << _sign << endl;
        _sign = s;    
        cout << _sign << endl;
    }
};



在C++中创建数组的时候需要声明数组的长度,在声明一个二维数组的参数时,则至少需要确认第二维的长度,否则就无法完成编译。 为什么呢,我们可以用一张图来表示c++二维数组在内存中的表示就理解了。

实际上在创建数组的时候,c++是根据最低维,也就是最靠后的那个维度最大值来分配连续内存空间的。譬如int[2][5]就会分配10*4个字节空间出来,如果不知道最后一个维度,c++就不知道如何开辟内存空间了。

二维数组返回的就是整个数组的首元素地址。 而访问则是根据最后维的长度进行运算后得出:

/*
 * c++ 二维数组
 * 
 * hello@shezw.com 2020.07.03
 */

#include <iostream>
#include <string>

using namespace std;

int main()
{
   int a[2][5] = {1,2,3,4,5,6,7,8,9,10};

    for( auto e:a ){
        printf( "%p : %d \n",e,*e );
    }
    printf( "%p : %d \n",&a[1][3],a[1][3] );
    printf( "%p : %d \n",&a[0][8],a[0][8] );

}

输出:

0x7fffa508a870 : 1 
0x7fffa508a884 : 6 
0x7fffa508a890 : 9 
0x7fffa508a890 : 9 

可以看到 a[0][8] 其实是完全等价于 a[1][3] 的,实际上a[1][3] 就是从第一个空间开始往后数第3+1*5 = 8个。

在数据结构、算法与应用一书中约定了一种动态创建二维数组的方式。

这种方式的核心是 先构造一维指针数组,再将每个指针指向对应列的首元素。

为了调用和使用方便,我这里设计一个Matrix模板类,专门用于这样的动态二维数组的使用。

/*
 * c++ 二维数组
 * 
 * hello@shezw.com 2020.07.03
 */

#include <iostream>
#include <string>

using namespace std;

template <typename T>
class Matrix{
private:
    T ** _elements;
    int _colSize;
    int _rowSize;

public:
    Matrix( int rows, int cols ){
        _colSize = cols;
        _rowSize = rows;
        _elements = new T * [rows];
        for( int i=0;i<rows;i++ ){
            _elements[i] = new T [cols]();
        }
    }

    ~Matrix(){
        for( int i=0;i<_rowSize;i++ ){
            delete [] _elements[i];
        }
        delete [] _elements;
    }

    int getSize(){ return _colSize * _rowSize; };
    int colSize(){ return _colSize; };
    int rowSize(){ return _rowSize; };

    // 函数形式
    const T & get( int row, int col ){
        return _elements[row][col];
    }
    // 重载操作符形式
    T* & operator[]( int row ){
        return _elements[row];
    }
    // 重载操作符形式 只读
    const T* & operator[]( int row) const{
        return _elements[row];
    }
    void print(){

        for( int i=0; i< _rowSize; i++ ){

            printf( "\n row %p: \n", _elements[i] );

            for( int j=0; j< _colSize; j++ ){
                printf( "  col %p - %d\n", &_elements[i][j], _elements[i][j] );
            }

        }

    }
};

int main()
{
   Matrix<int> m(3,5);
    m[2][1] = 15;
   m.print();
}

什么是循环?

循环就是反复的做一件事情,尽管每次都可能一模一样 也有可能略有差别。 无论是打扑克、还是打麻将,其实都是符合循环这个概念。

譬如说,打斗地主的时候我们每次都要不停的从牌堆中抓一张牌,大家分别取一张,直到牌堆里的牌只剩3张。

每一次的抓牌就是循环的一个过程。

那么for循环和while循环的差别是什么呢?

如果用女人可以理解的方式来说的话,那么

for循环是 曾经有一份真诚的爱情放在我的面前....... 我希望是 10000年

while循环是 山无棱、天地合,乃敢与君绝

正则表达式的重要性不言而喻,平时写的时候都是拼拼凑凑感觉还是不太好,趁着今天做一个梳理,要让正则的用法深入血液才好。

MDN | Javascript 正则表达式介绍

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

创建一个正则表达式节

你可以使用以下两种方法之一构建一个正则表达式:

使用一个正则表达式字面量,其由包含在斜杠之间的模式组成,如下所示:

var re = /ab+c/; 使用正则表达式字面量为正则表达式提供了脚本加载后的编译。当正则表达式保持不变时,使用此方法可获得更好的性能。

或者调用RegExp对象的构造函数,如下所示:

var re = new RegExp("ab+c"); 使用构造函数为正则表达式提供了运行时的编译。使用构造函数的方式,当你知道正则表达式的模式将会改变,或者你不知道模式,并且从其他来源获取它,如用户输入。

- 阅读剩余部分 -

最近正在做appsite框架传统网站架构的拓展、其中不免有SEO的部分。 在网上做了一点功课,meta中的 description比较关键,keywords据说已经不再受搜索引擎的关照了,不过这种事情现在不能盲目相信,既然做一个网站那么还是应该要照顾好关键词。但是没必要付出过高的人力成本,所以我这里想到的还是使用分词扩展来自动生成关键词。 这样做的好处是,无论搜索引擎是否在意关键词,我们有和内容相关度很高的关键词,这不会对我们带来损失。 基于程序算法生成的关键词,不需要我们花费人力成本,包括可以在后台控制是否开启该功能。

我这里选用的是对php支持比较好的 SCWS分词扩展。 如果分词的目的是更多的语义化分析、情感分析等,那还是应该考虑一下其他的分词库,不过基本上没有php支持。

进入正题

- 阅读剩余部分 -

因为最近的一个需求涉及到SEO 所以考虑用php写一套前台,这样可以根据url自动输出完整的页面,这里比较重要的是url rewrite 因为直接使用参数来访问页面一是不具备结构性,不方便基于文件夹管理php的前台页面,二是搜索引擎搜索方面也不友好。 所以要为后台添加一套自适应的rewrite。

以下是为一个SEO网站设计的结构

domain / home

  • /page | /class

  • /action

    • /id
  • ?querys

其URL示例为: https://domain/user/detail/12345/... https://domain/goods/list/... 经过重写转化为: https://domain/?abstract=user&functionality=detail&id=12345... https://domain/?abstract=goods&functionality=list...

redis没有表的概念

由于没有数据表的概念、我们很多基于关系型数据库的查询方式、在Redis服务器上就要有新的思路。 譬如说我们通过4张表来维护用户数据、在redis上就应该只使用一个Hash 另外类似于Comment这样的表,我们就需要设计成Redis List类型 以COMMET_USERID来作为key 每一次添加一条反序列化数据进去。但这样做面临的问题是我们无法使用关系型表的其他诸如合并查询、反向查询等查询功能。

redis服务密码验证访问

配置文件中配置 requirepass yourpassword

数据库层级的访问控制

目前没有方案 考虑中 这个部分也和java服务端的朋友讨论过,Redis确实没有这方面的支持,如果考虑同一个redis服务器中有多个或者大量Saas需求、那么比较好的方式就是在最前面加一个appid的索引。

探索一下几种实现方式:

# 以saasID作为key userid做其中一项的方式
hset TRFUnmsZ userid qFVqmqMz nickname sprite
hset gdYuSu4G userid hGE5REy2 nickname amber

# 以userid作为key saasid作为其中一项
hset qFVqmqMz saasid TRFUnmsZ nickname sprite
hset Lh8PCIVy saasid TRFUnmsZ nickname soki
hset gdYuSu4G saasid hGE5REy2 nickname amber

# 以saasid拼接 keyid的方式
hset TRFUnmsZ_qFVqmqMz nickname sprite
hset TRFUnmsZ_Lh8PCIVy nickname soki
hset hGE5REy2_gdYuSu4G nickname amber

hset TRFUnmsZ_SETTING WECHATID 123456
hset TRFUnmsZ_SETTING APPNAME AppSite
hset hGE5REy2_SETTING WECHATID 654321

基于Redis只能通过key进行查询、不能使用类似于MySQL的WHERE查询,所以用saasid作为key的方式被摒弃。我们不可能取出所有的saas下的数据 再到程序里查找。

- 阅读剩余部分 -

在macOS上安装php-redis是比较简单的,但是也有可能进入坑里,由于百度到的结果更坑,所以整理一下备用

1 安装redis

官方下载地址 https://redis.io/download 参考官方的说明、安装步骤是 手动下载或者使用终端wget下载tar包 下载完毕后解压并且打开目录

$ cd /User/sprite/Downloads/redis-5.0.5
$ make

安装完毕之后打开终端输入 redis-server 出现redis欢迎界面表示已经安装成功

使用redis-server启动redis服务之后不要关闭终端页面、测试的时候要另外打开一个终端、然后输入redis-cli 否则会报错 无法连接到redis服务器

2 安装phpredis扩展

大杀器 官方文档 phpredis-Github

参考官方的文档,我们可以采用pecl安装、我没有尝试。 使用编译安装的方式

2.1下载官方包到本地

https://github.com/phpredis/phpredis/releases

下载完毕后解压,建议把包转移到其他地方,防止后面清理下载文件夹清理掉。 比如说 我放到了 /Library/PhpExtra/redis-5.0.2 (可以随便放)

2.2 在终端打开这个目录
$ cd /Library/PhpExtra/redis-5.0.2/redis-5.0.2

一定要保证目录下有 configure文件

2.3 使用phpize进行安装

这一步是网上基本都错误的部分 phpize是一个可执行文件,路径一般在/usr/bin/下 如果我们的mac注册了php环境,那么我们直接输入phpize就可以 没有再切换目录的必要。

$ phpize ./configure --with-php-config=/usr/bin/php-config

如果这一步的时候,无法找到phpize我们再使用绝对路径来定位

$ /usr/bin/phpize ./configure --with-php-config=/usr/bin/php-config

可能发生的问题:

1.Cannot find autoconf. Please check your autoconf installation and the $PHP_AUTOCONF environment variable.Then, rerun this script.

brew install autoconf

2.mac phpize执行后报错:grep: /usr/include/php/main/php.h: No such file or directory

3.make: *** No targets specified and no makefile found.

如果phpize这一步成功了,不报错了。仍然有可能无法安装。比如说我就是这样。

无奈,尝试使用pecl方式安装。

一番折腾。 发现还是搞不定。 最终发现了是自己在2.3的步骤时候有错误。

划重点:

$ cd /Library/PhpExtra/redis-5.0.2/redis-5.0.2 # 切换到你解压的目录
$ phpize

Configuring for: PHP Api Version: 20160303 Zend Module Api No: 20160303 Zend Extension Api No: 320160303

这一步看不到版本号的,看上面错误参考2

$ ./configure

... configure: creating ./config.status config.status: creating config.h

$ make

... Build complete. Don't forget to run 'make test'.

$ make install

... Installing shared extensions: /usr/lib/php/extensions/no-debug-non-zts-20160303/ 最后一步会告诉你 扩展安装到了哪里,复制下来 去php的配置文件中添加

php.ini一般在 /etc/php.ini 如果你没有就复制一个 php.ini.default出来 最后加上:

extension_dir="/usr/lib/php/extensions/no-debug-non-zts-20160303"
extension=redis.so

保存重启apache

sudo apachectl restart

在网上查看很多的帖子都是在 hypedown模块中注释掉对 \n 的解析 这样的做法是不行的,因为这样文章中的换行就会失效,所有的文字就堆到一起了。

查看了服务端的源代码并没有太好的解决方案,官方论坛所谓给出的已解决的开发板,我换了一下也并没有解决,这里先给一个临时解决方案。

在主题的footer中加入一行js代码即可

document.querySelectorAll(".post").forEach(function(post){ post.innerHTML = post.innerHTML.replace(RegExp(/<br><br>/g), '') });

主要就是检测同时出现两行的换行 就将它剔除掉 当然不完美,不过也没必要花太多时间在这个部分,大体上解决了问题~

接近14年底的时候我刚从西藏和尼泊尔回来,那时候我一直在筹划一个好课网的 互联网学习平台,在淘宝上做了0.5套 高水平的ps课程,那个时候我自己有点隐忧,觉得自己内心并没有充满了信心,其实就是感觉还没到。

那个时候想到一些点子,想做一些爆款小游戏,比如说我之前跟你提到过的 摇手机打boss 那个概念,因为我希望可以先做点小游戏做些流水出来,之后再考虑做大,做纯互联网的公司,因为互联网太难做了,需要把盘子做很大,而前期发展又非常耗钱,并且我找过一些程序的朋友,他们都委婉的拒绝了,或者答应了又没音信了,找了一些做过ios游戏的朋友,也给的是冷水。 而刚刚好这个时候,soki的一个朋友小艾 ,突然问我 在做什么等等并且拉我出来聊,soki这个游戏创业的事情。

我首先认为我是做设计的,做游戏美术并不擅长,并且带上了我认为我能做和不能做的部分,比如说那种超级写实,中世纪,超级立体等等,我说我做不了,但是我可以做各种酷炫,设计感好的游戏。 他当时看了看和我强调他很看重游戏的UI设计等等,可能也是因为他一贯在互联网公司的原因,我们聊的比较来,我们都比较认同剑走偏锋这样的价值观。 所以我就觉得这个事情可以做,当时他跟我谈过所谓的待遇或者未来可能有的报酬 大约什么一年30多万吧,股权15% 这样。我当时其实没太感冒,我是直接告诉他 可以先开始合作一段时间,就这样一直给他免费干到第二年看到你。 这个部分有几个点:

  1. 我觉得soki和我的大方向比较吻合,而且他在钱这个问题上,比我更有注意力,因为我一直觉得自己的软肋是财务方面,所以我觉得如果和他一起创业,至少可以让我在这方面得到进步。
  2. 我不够自信,不觉得自己做公司可以成功。
  3. 做好课网遇到压力和瓶颈,有一些想要逃避
  4. 对于当初那些泼冷水和不认可我做小游戏的人,我刚好这个机会可以证明自己 (为了创业而创业)。

- 阅读剩余部分 -

"QuartzComposer并没有传言中那么方便,FramerJS更令人惊喜。"

Spy_feature.jpg

最近在负责一款SpyCamera的设计和交互,因为本身这款软件是一个非常小众而又轻量级的小应用,在众多竞争者中除了核心的功能安排外,能够进行差异化设计的就只有视觉设计和交互设计了。 在视觉设计这部分我把ICON和UI视觉全部重构了一遍,在重构的过程中由于需要去贴合安卓5.0的设计规范来获得谷歌一定的推荐流量,所以把整个视觉设计的相对轻、薄、简,这时一个优秀又有特色的交互就显得更有吸引力了。

加之之前看过一些关于QuartzComposer的介绍,总算找到一个机会好好花点时间来琢磨一下交互设计的实现部分。 首选当然是已经让我激动已久的QuartzComposer ,上手还真不轻松,在网上搜集了一些资料,然后一步步的安装和尝试。

资料传送:

官方资料 (图片演示和DEMO文件下载)

CSDN较为详细的介绍和说明 (比较细致的中文说明)

苹果官方的指导文档  QuartzComposerUserGuide.pdf

豆瓣上的新手上路

Github上的新手上路  quartz-composer-and-origami-tutorial-button-animation (英文)

知乎上 几款 高保真交互设计软件的对比

- 阅读剩余部分 -

"当自己的目标非常遥远和困难的时候,更需要克制自己的内心,平衡理想和现实的差距,放慢心境,在正确的方向上稳步前行,这一切,都是为了更有效率的去达成那个最遥远的理想。"

近期解决了几件大事,之前的RunPuppyRun游戏重构和优化的工作完成以及新版本上线,原画师(概念设定)一职的招聘和选定人选,以及团队的优劣势发觉以及协作的磨合。 写在今天是因为总算把原画师一职顺利落定,在组织完部门小会之后的现在我才可以静下心来写一写最近的感受。

- 阅读剩余部分 -

“一次细致深入的整理带来的价值远远超过在具体工作中能直接带来的效果,更多的是帮助我在思考的过程中更加明晰目标,判断方向以及正确的做减法。”

正在整理着手头的RunPuppyRun 的 Artwork Resource Total List 以及 参考项目 KiwiDash 的 Artwork Resource Total List,想到一些比较重要的点,所以写下来。 因为着实没怎么做过游戏,特别是没有深入的从头至尾的参与过完整的游戏项目,在当下继续深抠RunPuppyRun这个项目时,遭遇到一个很大的瓶颈就是欠缺细节的洞察和贯彻执行能力。 在今天之前我们花了足足两周多的时间来进行项目重构,技术代码重新写了一遍,UI也全部重新画了,角色动画也一直在调,但还没有一个满意的结果。 我们多次开会讨论并试图去找到我们的核心1+X,分析用户的行为数据和交互数据,我们用了各种办法去找到我们想要的答案。当然包括下面这张用户的交互层流转分析。 Run_Data.jpg

- 阅读剩余部分 -