2017年3月24日 星期五

Android Studio 一鍵部署安裝到多台電腦的解決方案

Android Studio 一鍵部署解決方案


我拆分四個安裝檔案分別是
  1. Java JDK
  2. 軟體設定檔
  3. 主程式
  4. SDK
將他們用RAR封裝,並自動執行批次檔設定必要的環境變數與操作。

JAVA

比較有問題的是沒有經過官方的安裝程式安裝,除了環境變數要新增之外,還要新增 JAVA_HOME 的變數,這個變數只到JDK的資料夾
@echo off
Title JAVA_PATH By Charlotte.HonG& Color 1A

set str=%~dp0jdk1.8.0_101
setx /m JAVA_HOME "%str%"

set str=%PATH%;%~dp0jdk1.8.0_101\bin;
setx /m PATH "%str%"

exit
這個批次檔放在JDK的目錄內,利用rar封裝成exe解壓縮之後自動執行bat即可自動設定好路徑。

軟體設定檔

儲存在使用者文件內的 .android.AndroidStudio2.3 複製他們你可獲得完全相同的設定,而不用安裝之後還要進入程式內設定。
比較需要注意的是有一些檔案不能保留
.android 內的 avd 要清空,他沒有辦法被移植,必須重新建立
.AndroidStudio2.3 內的 system\caches 要清空讓目標電腦自己重新建立

主程式

一般裝在 C:\Program Files\Android 直接複製就可以用了

SDK

也是直接複製就可以用了,只是最好路徑要放到目標電腦一樣的位置,否則需要重新設定路徑。
這個容量很大沒辦法封裝成exe,我是壓縮成rar並利用命令解壓縮。
@Echo 解壓縮Android SDK檔
::設定環境變數
path=%path%;C:\Program Files\WinRAR;
::解壓縮檔案到指定位置
d:
::rar x "%~dp0AndroidSDK.rar" -r -o+

最後

將他們完整的複製到另一台電腦就可以直接移植了,我是將他們分別用rar封裝成exe自動解壓到目標位置,並設定成隱形模式利用一個批次檔依序執行4個壓縮檔。
Rem By CharlotteHonG
@Echo Off
Title Android Studio All Install & Color 1A
Rem 確認是否為管理員權限
call :IsAdmin
:: =================================================================
@Echo:**確認後請繼續**
Pause
@Echo 開始安裝...
start "" /wait "%~dp001_jdk1.8.0_101_AutoInstall.exe"
start "" /wait "%~dp002_Andrio Studio Setting.exe"
start "" /wait "%~dp003_Android Studio.exe"
@Echo 完成軟體安裝

@Echo 解壓縮Android SDK檔
::設定環境變數
path=%path%;C:\Program Files\WinRAR;
::解壓縮檔案到指定位置
d:
::rar x "%~dp0AndroidSDK.rar" -r -o+

@Echo 完成所有程序
set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_101
start "" "C:\Program Files\Android Studio\bin\studio64.exe"
Exit
:: =================================================================
:IsAdmin
Reg.exe query "HKU\S-1-5-19\Environment"
If Not %ERRORLEVEL% EQU 0 (
 Cls & 權限不足,請使用管理員全線重新開啟。
 Pause & Exit
)
Cls
goto:eof
:: =================================================================

例外設定

值得注意的是這樣子移植之後會有一個設定會跑掉,需要手動設定回來。

2017年3月23日 星期四

operator() 進階用法 繼承

operator() 進階用法 繼承

如果你有兩個以上的陣列類別,你就必須為他們個別重載所有的 operator 運算子,又他們很有可能都是完全一樣的,你只是想對那個陣列做運算不會動到其他成員,他們的差別只在於型態不一樣,彼此互相有不同的資料成員。
最好的解法就是把他們的陣列拉出來用template實作,並為他們重載需要的 operator 然後再用正確的型態繼承到個別的類別。
比如說父類別為
Arr<T>
子類別為
Arr_int
Arr_double
Arr_char
我們總不可能為了這三個個別寫了各自完全一樣,只有型態不行的運算子重載,一個 + 就需要用 +=+ ,如果還做了常數的相加
Arr_int a;
a=a+1;
a=1+a;
又需要多寫2個重載函式,相當於一個運算符號需要4個函式,總共有4個符號所以是16個,我們做了3個類別需要寫了16*3個。
繼承後會遇到一些問題,這裡附上可能發生的問題與他的解法

資料結構

這是我們的主類別
template <typename T>
class Arr {
    Arr(initializer_list<T> i): arr(i){}
    Arr & operator+=(Arr const& rhs);
protected:
    vector<T> arr;
};
Arr<T1> operator+(Arr<T1> const &lhs, Arr<T1> const &rhs){...}
這是繼承後的子類別
class Arr_int: public Arr<int>{
public:
    Arr_int(initializer_list<int> i): Arr<int>(i), i(-1){}
public:
    int i;
};

上下轉型的問題

加號產生的暫存父類別

這個問題發生在某些情況,或某些情況必然會遇到分別是
Arr_int a{0}, b{0};
a = b+b;
這裡因為我們的 + 函式的定義返還值為 Arr<int>Arr_int 不符合會導致
Arr_int = Arr<int>

讓他等於父類別

Arr<int> f{0};
Arr_int a{0};
a = f;

原因與解法

父類別型態出現在等號右邊,可是函式內預設只會建立
arr_int & arr_int::operator=(arr_int const & rhs);
他會找不到函式呀,我們需要多載正確的型態等號
Arr_int & operator=(Arr<int> const & rhs);
至於裡面的實作我們可以呼叫父類別來做
Arr_int & operator=(Arr<int> const & rhs){
   this->Arr<int>::operator=(rhs);
   // other ...

   return (*this);
}
這會導致子類別的多出來的成員不會被操作到,如果需要用上則在這裡補上。加號也是一樣如果需要的話則用這種方式減少代碼的重複。

繼承後如何轉名

這種做法會導致所有的子類別名稱都是
vector<T> arr;
我們可以透過參考的方式對他重新命名,取一個別名
public:
    // 更換別名
    decltype(arr) & mask = arr;
如此一來就可以自由地操作了。

實作範例

inhereit.hpp

/*****************************************************************
Name : 
Date : 2017/03/17
By   : CharlotteHonG
Final: 2017/03/17
*****************************************************************/
#include <iostream>
#include <vector>
#include <initializer_list>
using namespace std;

template <typename T>
class Arr {
public:
    Arr(initializer_list<T> i): arr(i){}
    Arr & operator=(Arr<T> const & rhs) = default;
public:
    void pri(string name=""){
        cout << name << " = ";
        for(auto&& i : arr) {
            cout << i << ", ";
        }cout << endl;
    }
public:
    // 重載下標符號
    T & operator[](size_t idx){
        return const_cast<T&>(static_cast<const Arr&>(*this)[idx]);
    }
    const T & operator[](size_t idx) const{
        return arr[idx];
    }
    // 重載+=符號
    template <typename T1> friend
        Arr<T1> operator+(Arr<T1> const &lhs, Arr<T1> const &rhs);
    Arr & operator+=(Arr const& rhs){
        for(unsigned i = 0; i < arr.size(); ++i)
            (*this)[i] += rhs[i];
        return (*this);
    }
    Arr & operator+=(T const &rhs){
        for(unsigned i = 0; i < arr.size(); ++i)
            (*this)[i] += rhs;
        return (*this);
    }
protected:
    vector<T> arr;
};
// 重載+符號(全域函式)
template <typename T1>
Arr<T1> operator+(Arr<T1> const &lhs, Arr<T1> const &rhs){
    return Arr<T1>(lhs) += rhs;
}
//----------------------------------------------------------------
class Arr_int: public Arr<int>{
public:
    Arr_int(initializer_list<int> i): Arr<int>(i), i(-1){}
    Arr_int(Arr<int> const & rhs):Arr<int>(rhs), i(-1){}
    Arr_int & operator=(Arr<int> const & rhs){
        this->Arr<int>::operator=(rhs);
        return (*this);
    }
public:
    int i;
};

using uch = unsigned char;
class Arr_uch: public Arr<uch>{
public:
    // 更換別名
    decltype(arr) & mask = arr;
public:
    Arr_uch(initializer_list<uch> i): Arr<uch>(i){}
    Arr_uch(Arr<uch> const & rhs):Arr<uch>(rhs){}
    Arr_uch & operator=(Arr<uch> const & rhs){
        (*this).Arr<uch>::operator=(rhs);
        return (*this);
    }
};
//----------------------------------------------------------------

inhereit_main.cpp

/*****************************************************************
Name : 無損繼承 operator
Date : 2017/03/17
By   : CharlotteHonG
Final: 2017/03/17
*****************************************************************/
#include <iostream>
#include "inhereit.hpp"
using namespace std;
/*==============================================================*/
int main(int argc, char const *argv[]){
    Arr<int> a{0, 1, 2};
    Arr<int> b{0, 1, 2};
    b=b+a;
    b.pri("b+a");

    // 繼承運算符號
    Arr_int c{1, 1, 1};
    Arr_int c1{1, 1, 1};
    c.i=7;
    c1.i=8;
    c = c1+c1;
    cout << c.i << " || ";
    c.pri("c1+c1");

    // 繼承的 += 操作符號不會操作子類別成員
    c += c1;
    cout << c.i << " || ";
    c.pri("c+=c1");

    // 複製建構子
    Arr_int c2(c1);
    cout << c2.i << " || ";
    c2.pri("c2");
    Arr_int c3(c+c); // c+c 返還的是父類別
    cout << c3.i << " || ";
    c3.pri("c3");

    // 繼承運算符號2
    Arr_uch d{'0', '0', '0'};
    Arr<unsigned char> ch{'0', '0', '0'};
    d = d+d;
    d.pri("d+d");

    return 0;
}
/*==============================================================*/

未定義行為與未指定行為的差異

未定義行為與未指定行為的差異

tags: C++ Concept
未定義行為(Undefined behavior)未指定行為(Unspecified behavior) 的差異

未定義行為(Undefined behavior)

可能發生的事情
  • 來自語言標準定義的A行為
  • 來自語言標準定義的B行為
  • 編譯器作者的行為
編譯器可以根據語言標準所提供的行為,選擇要執行哪個行為
不會炸且保證每次都一樣,但是有可能換一個編譯器得到不一樣的結果

未指定行為(Unspecified behavior

可能發生的事情
  • 編譯器作者的行為
  • 出錯炸了
未定義行為則是完全看你運氣了,他就是存在那個會炸掉的機率,而且還很大。
但如果你的程式寫的足夠多,或執行的足夠久,炸的機率接近100%。

總結

總結來說如果你在不移植的情況下未指定行為是安全的
未定義行為則是絕對要避免的

2017年3月22日 星期三

Skype 登入後一直轉圈圈不會好

Skype 登入後一直轉圈圈不會好

今天早上起來skype忽然一轉圈圈不會好,不知道怎麼了,重新開機也不會好;後來我先嘗試複製歷史信息到令另一台電腦,很怕是聊天信息太多壞了,還好複製過去是正常的,著手嘗試其他辦法。
把Skype手動登出再登入居然就好了
後來想想可能發生的原因,應該是因為昨天晚上電腦有按休眠,休眠常常會出事情呢FB的聊天軟體也會因為休眠而區要重啟。

虛擬函式的實際用途 與 上下選型的概念

虛擬函式的實際用途 與 繼承時上下轉型的概念

tags: C++ Concept
  1. 敘述什麼時候必須、一定要使用虛擬函式,以及他真正的用途
  2. 敘述什麼是上下轉型及說明dynamic_cast的操作時機以及他與 static_cast 有什麼不同

虛擬函式

虛擬函式並不是,能夠讓函式被繼承這麼簡單,這是從答案反推的單向結果論

檢視

正確的形容方式應該說成,在以指針檢視時,能夠依據正確的 類型 自動套用正確的函式使用

選擇

virtual 可以把他當作可有可無的低優先權命令,當編譯器使用指標檢視遇到
目前儲存型態實際型態 時自動選擇實際的型態函式

示例

這麼說其實還是有點不好理解,實際由以下代碼來說明
代碼結構
class A {
public:
    A(){}
    void virtual fun(){
        cout << "this is A" << endl;
    }
};

class B: public A{
public:
    B(){}
    void fun(){
        cout << "this is B" << endl;
    }
    int i=1;
};

當我們使用指針檢視(操作)時
A *a, *b;

a = new A;
b = new B;
這裡的 A 我把它稱為 目前儲存型態 ,也就是我們告訴編譯器,會已 A 的角度(型態),來檢視(操作)這個指針
可注意到 b 它的實際型態應該是 B 但是被強迫只用 A 型態檢視,這裡稱為向上轉型,也就是 b 的實際型態應該為 B

(*a).fun();
目前 a 的檢視型態為 A 所以可以正確地呼叫 A 裡面的成員函式,這很容易理解並沒有什麼問題。

(*b).fun();
這裡就有問題了,b 的實際型態是B,可是卻被宣告用 A 的角度來檢視,一般來說會直接呼叫 A 的函式成員
但如果在 A 的資料函式加上 Virtual ,將可以自動選擇實際的 B 函式

上下轉型

在本範例中 A 稱為父類別 B 稱為子類別
  • 向上轉型:由子類別轉型為父類別(或再上一層)
  • 向下轉型:由父類別轉型為子類別(或再下一層)
他們之間的關係具有以下特性
  • B 繼承了 A 的所有資源,A 有東西 B 也一定有
  • B 新增的資料成員與函式 A 不會擁有
依據剛剛的 指針檢視行為 可以把想像成是我要檢視到哪裡。
(註:只是我自己想到的一個類比說明方法)
比如說的資料的地址長度可能是這樣
  單位 1    單位2
---------------------
  A 地址  | xxxxxxx
---------------------
  B    地      址
---------------------
          |    i
---------------------
可以看到 B 的角度檢視的範圍比較長一些,如果你用 A 的角度檢視,就無法見到變數 i 。
由此可見,你要將任何子類別向上轉型成父類別都不會出狀況的,只不過會無法存取(不會丟失),所有自己額外擁有的成員。

向下轉型可能造成非法存取

但是如果你要做向下轉型就會出問題的,如果他當初建立的時候就是用父型態建立,你將檢視到沒有被初始的垃圾範圍,而且這就像是越界存取一樣是非法存取。
為了避免這個問題可以使用 dynamic_cast<typename*>(T) 編譯器將自動檢視是否為合法行為。

安全的向下轉型

剛剛我們的 b 地址所初始化的為子類別型態初始化,只不過我們宣告成父類別的檢視行為。
我們可以將他安全的向下轉型,因為他當初初始的話就是一個 B 的型態,轉型後不會造成越界存取的非法行為。
// 向下轉型
cout << "(*b).i=" << dynamic_cast<B &>(*b).i << endl;
如果你很確定保證絕對不會出錯那麼也可以使用 static_cast<>
// 向下轉型
cout << "(*b).i=" << static_cast<B &>(*b).i << endl;
在向上轉型時,因為保證不會出意外所以使用兩者不會有什麼區別,只不過使用 dynamic_cast<> 可以具備上下轉型的閱讀語意,可能比較容易閱讀代碼。

非法的向下轉型

// 非法的向下轉型
cout << "(A->B). = " << static_cast<B &>(*a).i << endl;
這將會導致越界的非法存取,程式會崩潰沒有回應

範例代碼

/*****************************************************************
Name : 
Date : 2017/03/22
By   : CharlotteHonG
Final: 2017/03/22
*****************************************************************/
#include <iostream>
using namespace std;

class A {
public:
    A(){}
    void virtual fun(){
        cout << "this is A" << endl;
    }
};

class B: public A{
public:
    B(){}
    void fun(){
        cout << "this is B" << endl;
    }
    int i=1;
};

/*==============================================================*/
int main(int argc, char const *argv[]){
    // 用父類別來操作所有指標
    A *a, *b;
    a = new A;
    b = new B;

    // 自動選擇正確的類型
    (*a).fun();
    (*b).fun();

    // 無法存取 (找不到該成員)
    // cout << "(*b).i=" << (*b).i << endl; // Error

    // 安全向下轉型
    cout << "(*b).i = " << dynamic_cast<B &>(*b).i << endl;

    // 非法的向下轉型
    cout << "(A->B). = " << static_cast<B &>(*a).i << endl;

    return 0;
}
/*==============================================================*/