鱼C论坛

 找回密码
 立即注册
查看: 2501|回复: 17

[技术交流] C++

[复制链接]
发表于 2019-8-1 21:03:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 bin554385863 于 2019-8-3 17:42 编辑

2019年8月1日20:55:53
2019年8月3日17:43:55 修改类的定义


类的定义
class mycomplex
{
private:
    double re, im;//类的数据

public:
    mycomplex(double r = 0, double i = 0)
        : re(r), im(i)
    {
        ;
    }

    double real() const//成员函数
    {
        return re;
    }

    double imag() const//成员函数
    {
        return im;
    }
};


关键字:vector

快速动态数组,可以再运行阶段扩充数组

可以插入,删除,索引数组数据

定义:


vector<date type> datename;

vector<date type> datename(size, date)
向datename中写入size个date
360截图20190801211436699.jpg

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2019-8-8 00:53:22 | 显示全部楼层
本帖最后由 bin554385863 于 2019-8-14 00:12 编辑

2019年8月8日00:52:30
函数指针的用法
#include <iostream>
using namespace std;
/*加法 */
int add(int arg1, int arg2)
{
    return arg1 + arg2;
}
/*乘法 */
double mult(double arg1, double arg2)
{
    return arg1 * arg2;
}
/*减法 */
int subt(int arg1, int arg2)
{
    return arg1 - arg2;
}
/*除法 */
double divi(double arg1, double arg2)
{
    return arg1 / arg2;
}

void calc(int arg1, int arg2)
{
    double res = 0;
    int (*pcalc)(int, int);
    double (*pcalc_c)(double, double);
    char op;
    cout << "请输入运算式" << endl;
    cin >> arg1>>op>>arg2;
    switch (op)
    {
    case '+':
        pcalc = add;
        res = pcalc(arg1, arg2);
        break;
    case '-':
        pcalc = subt;
        res = pcalc(arg1, arg2);
        break;
    case '*':
        pcalc_c = mult;
        res = pcalc_c(arg1, arg2);
        break;
    case '/':
        pcalc_c = divi;
        res = pcalc_c(arg1, arg2);
        break;

    default:
        break;
    }
    cout << res << endl;
}
int main(int argc, char const *argv[])
{
    int b = 0, c = 0;
    calc(b , c);
    return 0;
}
-----------------------------------------------------------------------------------------------------------------------------------------------------
Windows PowerShell
版权所有 (C) Microsoft Corporation。保留所有权利。

PS E:\Users\86184\Documents\Code> & 'c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe' '--stdin=Microsoft-MIEngine-In-vxyzpdfk.c0n' '--stdout=Microsoft-MIEngine-Out-nlc2nijy.y3z' '--stderr=Microsoft-MIEngine-Error-exg2ivwk.zjt' '--pid=Microsoft-MIEngine-Pid-phhor1q0.0vm' '--dbgExe=E:\My Program\MinGW\bin\gdb.exe' '--interpreter=mi'
请输入运算式
3 +9
12
PS E:\Users\86184\Documents\Code> & 'c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe' '--stdin=Microsoft-MIEngine-In-3hgfugpp.4lk' '--stdout=Microsoft-MIEngine-Out-mdgp1rln.unc' '--stderr=Microsoft-MIEngine-Error-cwc0ujp0.sck' '--pid=Microsoft-MIEngine-Pid-vnhjpifc.de1' '--dbgExe=E:\My Program\MinGW\bin\gdb.exe' '--interpreter=mi'
请输入运算式
32*96
3072
PS E:\Users\86184\Documents\Code> & 'c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe' '--stdin=Microsoft-MIEngine-In-0aqcrvwu.njk' '--stdout=Microsoft-MIEngine-Out-xamzervn.uy0' '--stderr=Microsoft-MIEngine-Error-sx33tg4t.0su' '--pid=Microsoft-MIEngine-Pid-x10t3xb4.5ex' '--dbgExe=E:\My Program\MinGW\bin\gdb.exe' '--interpreter=mi'
请输入运算式
56-98
-42[/color
============================================================================================
#include <iostream>
using namespace std;
/*加法 */
double add(double arg1, double arg2)
{
    return arg1 + arg2;
}
/*乘法 */
double mult(double arg1, double arg2)
{
    return arg1 * arg2;
}
/*减法 */
double subt(double arg1, double arg2)
{
    return arg1 - arg2;
}
/*除法 */
double divi(double arg1, double arg2)
{
    return arg1 / arg2;
}

void calc(double arg1, double arg2)
{
    while (1)
    {
        double res = 0;
        double (*pcalc)(double, double);
        char op;
        cout << "请输入运算式" << endl;
        cin >> arg1 >> op >> arg2;
        switch (op)
        {
        case '+':
            pcalc = add;
            res = pcalc(arg1, arg2);
            break;
        case '-':
            pcalc = subt;
            res = pcalc(arg1, arg2);
            break;
        case '*':
            pcalc = mult;
            res = pcalc(arg1, arg2);
            break;
        case '/':
            pcalc = divi;
            res = pcalc(arg1, arg2);
            break;

        default:
            break;
        }
        cout << arg1 << op << arg2 << '=' << res << endl;
    }
}
int main(char argc, char const *argv[])
{
    int b = 0, c = 0;
    calc(b, c);
    return 0;
}
----------------------------------------------------------------------------------------------------------------------

PS E:\Users\86184\Documents\Code> & 'c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe' '--stdin=Microsoft-MIEngine-In-alch3axa.k5g' '--stdout=Microsoft-MIEngine-Out-20tna1oi.b2f' '--stderr=Microsoft-MIEngine-Error-gxxl5wak.ars' '--pid=Microsoft-MIEngine-Pid-bf3fmh1m.cx1' '--dbgExe=E:\My Program\MinGW\bin\gdb.exe' '--interpreter=mi'
请输入运算式
23+63
23+63=86
请输入运算式
63*98
63*98=6174
请输入运算式
63/54
63/54=1.16667
请输入运算式
87-96
87-96=-9
请输入运算式
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-11 11:25:26 | 显示全部楼层
本帖最后由 bin554385863 于 2019-8-11 12:02 编辑

2019年8月11日11:23:22
引用&指针
#include <iostream>
#include <cstdio>
int main(int argc, char const *argv[])
{
    char str = 'p';
    /*引用 */
    char &Refstr = str;
    std::cout << "初始值 " << '\n'
              << "str "
              << "= " << str << std::endl;
    Refstr = 'f';
    std::cout << "引用改值 " << '\n'
              << "str "
              << "= " << str << std::endl;
    /*指针 */
    char *pstr = &str;
    *pstr = 'o';
    std::cout << "指针改值 " << '\n'
              << "str "
              << "= " << str << std::endl;
    std::cout << "指针本身的值" << '\n'
              << "pstr "
              << "= " << std::hex << (void *)pstr;
    //printf("%#X", pstr);
    return 0;
}
-----------------------------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-5edmn1ex.z4c --stdout=Microsoft-MIEngine-Out-dd5gwwiv.yrl --stderr=Microsoft-MIEngine-Error-auibg2v3.efm --pid=Microsoft-MIEngine-Pid-x2jmfct3.4uu "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
初始值
str = p
引用改值
str = f
指针改值
str = o
指针本身的值
pstr = 0x61ff17

E:\Users\86184\Documents\Code>


引用不是对象因此不能对它取地址,他只是拥有指针功能的,变量的一个别名,就像人的外号,而指针则相当于身份证号
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-12 19:39:25 | 显示全部楼层
本帖最后由 bin554385863 于 2019-8-14 00:13 编辑

2019年8月12日19:35:37

C++11 auto和decltype关键字
#include <iostream>
#include <typeinfo>
using namespace std;
int main(int argc, char const *argv[])
{
    float const b = 100u;//b是常量不能改变它的值
    auto a = b;//auto通过数据的初始值来判断变量的类型。auto忽略了b的const声明
    a = 0;//变量a不是常量,可以改变它的值
    decltype(b) c = 0;//decltype返回操作数的类型,并使用它定义别的变量.c是常量它的值无法改变。decltype不会忽略const关键字。
    decltype(a) e;//e不是常量
    cout<<typeid(c).name()<<endl;
    /*它俩的区别是:auto会忽略左值的const声明。decltype()则不会*/
    return 0;
}
--------------------------------------------------------------------------------------Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-wmpqbpum.apa --stdout=Microsoft-MIEngine-Out-iq32vdue.mej --stderr=Microsoft-MIEngine-Error-veqrbw3z.oya --pid=Microsoft-MIEngine-Pid-jufr4wdn.vvm "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
f

E:\Users\86184\Documents\Code>

==================================================================
因为是mingGW所以输出的是类型缩写,用VS则会输出全称。

decltype((a)):双层括号返回的是引用类型,相当于
int &b = a;
decltype(b);

要想用auto定义一个常量必须加const关键字,
const auto c = 0;

==============================================

C++11范围for循环
#include <iostream>
/*范围for循环*/
int main(int argc, char const *argv[])
{
    using std::cout;
    using std::endl;
    int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    for (int num : arr) //for(存储数组元素的临时变量 :数组指针)
    {
        cout << num << ' ';
    }
    return 0;
}
-----------------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-lacyeeus.y5c --stdout=Microsoft-MIEngine-Out-fukloctk.1nv --stderr=Microsoft-MIEngine-Error-j2zzod5r.wrd --pid=Microsoft-MIEngine-Pid-g3nrssx2.sku "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
0 1 2 3 4 5 6 7 8 9
E:\Users\86184\Documents\Code>

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-13 22:52:19 | 显示全部楼层
本帖最后由 bin554385863 于 2019-8-14 00:13 编辑

2019年8月13日22:43:21
#include <iostream>
#include <string>
#include <cctype>
int main(int argc, char const *argv[])
{
    using namespace std;
    string str1 = "hello";
    string str2 = "world";
    string strc, strg;
    /*getling可以读取空格,以回车结束*/
    getline(cin, strg);
    /*cin遇到空白字符就结束读取*/
    cin >> strc;
    cout<<endl;
    cout << "str1 = " << str1 << endl;
    cout << "str2 = " << str2 << endl;
    cout << "strc = " << strc << endl;
    cout << "strg = " << strg << endl;
    /*string类型的字符串可以用加号拼接,用关系运算符进行比较*/
    str1 = str1 + " " + str2 + " " + strg + " " + strc;
    cout << "str1 = " << str1 << endl;
    decltype(str1.size()) count = 0;
    /*范围for循环*/
    for (char c : str1)
    {
        /*ispunct()判断字符是不是符号*/
        if (ispunct(c))
        {
            count++;
        }
        /*toupper()将小写字符转换成大写字符*/
        cout << (char)toupper(c);
    }
    cout << "总共有" << count << "个符号";
    return 0;
}
------------------------------------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-br3thiie.l4p --stdout=Microsoft-MIEngine-Out-k0qugrwq.5k1 --stderr=Microsoft-MIEngine-Error-cbjsa24n.1s2 --pid=Microsoft-MIEngine-Pid-zsskfzrp.cbo "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
c language
c++!!!

str1 = hello
str2 = world
strc = c++!!!
strg = c language
str1 = hello world c language c++!!!
HELLO WORLD C LANGUAGE C++!!!
总共有5个符号

E:\Users\86184\Documents\Code>

=================================================================================
360截图20190813225604107.gif

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-14 19:08:39 | 显示全部楼层
本帖最后由 bin554385863 于 2019-8-15 12:16 编辑

2019年8月14日19:07:56
向量vector的初始化
#include <iostream>
#include <vector>
#include <string>
int main(int argc, char const *argv[])
{
    using namespace std;
    /*列表初始化*/
    vector<string> str = {"hello world"};
    /*构造初始化*/
    vector<string> str1(10, "A");
    /*读取输入初始化*/
    vector<string> str2;
    string cintxt;
    while (getline(cin, cintxt))
    {
        str2.push_back(cintxt);
        if (getchar() == '\n')
        {
            break;
        }     
    }
    cout << "列表初始化: "
         << "str = ";
    for (auto strc : str)
    {
        cout << strc;
    }
    cout << endl;

    cout << "构造初始化: "
         << "str1 = ";
    for (auto str1c : str1)
    {
        cout << str1c;
    }
    cout << endl;

    cout << "读取输入初始化: "
         << "str2 = ";
    for (auto str2c : str2)
    {
        cout << str2c;
    }
    cout << endl;
    return 0;
}
*****************************************************************************
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-cz0l4q3a.zml --stdout=Microsoft-MIEngine-Out-vxgsh2vk.r4p --stderr=Microsoft-MIEngine-Error-nuzcpwcp.1nd --pid=Microsoft-MIEngine-Pid-2hpomys3.ecs "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
****** ********* ********** **********

列表初始化: str = hello world
构造初始化: str1 = AAAAAAAAAA
读取输入初始化: str2 = ****** ********* ********** **********

E:\Users\86184\Documents\Code>

===========================================================================
练习
#include <iostream>
#include <cctype>
#include <vector>
#include <string>
/*输入一组字符串,转换成大写字符后,按横输出*/
int main(int argc, char const *argv[])
{
    using namespace std;
    /*临时变量*/
    string word;
    /*向量str*/
    vector<string> str;
    cout << "请输入字符串" << endl;
    /*临时变量接受输入*/
    getline(cin, word);
    /*将临时变量的数据加入向量str*/
    str.push_back(word);
    /*加入换行符并转换大小写*/
    cout<<endl;
    cout<<"输出结果"<<endl;
    for (string c : str)
    {
        for (char b : c)
        {
            
            if (b == ' ')
            {
                b = '\n';
            }
            else
            {
                b = (char)toupper(b);
            }
            cout<<b;
        }
    }
    return 0;
}
***************************************************************************
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-4s4jmhdg.mpa --stdout=Microsoft-MIEngine-Out-qizf4ybs.qzw --stderr=Microsoft-MIEngine-Error-yfusqgvx.q0o --pid=Microsoft-MIEngine-Pid-fshxcw4u.gg2 "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
请输入字符串
hello c++ language you are crazy

输出结果
HELLO
C++
LANGUAGE
YOU
ARE
CRAZY

E:\Users\86184\Documents\Code>

=============================================================================================
练习
#include <iostream>
#include <vector>
#include <cctype>
/*定义一个vector对象,读取一组数字,计算相邻元素的和,和头尾相加的和*/
int main(int argc, char const *argv[])
{
    using namespace std;
    int a = 1;
    vector<int> veca;
    /*输入数据*/
    cout << "请输入数据" << endl;
    while (a)
    {
        cin >> a;
        /*舍去结束符0*/
        if (!a)
        {
            break;
        }
        veca.push_back(a);
    }
    /*查看向量的元素*/
    cout << "对象元素" << endl;
    for (auto a : veca)
    {
        cout << a << "  ";
    }
    cout << endl;
    decltype(veca.size()) i = 0, s = veca.size();
    /*计算相邻两个元素的和*/
    cout << "相邻元素的和" << endl;
    for (; i < s - 1; i++)
    {
        cout << veca[i] << " + " << veca[i + 1] << " = " << veca[i] + veca[i + 1] << "    ";
    }
    cout << endl;
    /*计算头尾相加的和*/
    cout << "头尾相加的和" << endl;
    for (i = 0; i < (s + 1) / 2; i++)
    {
        cout << veca[i] << " + " << veca[s - 1 - i] << " = " << veca[i] + veca[s - 1 - i] << "   ";
    }
    return 0;
}
***********************************************************************************************
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-t40m2dyf.5po --stdout=Microsoft-MIEngine-Out-kosvuagf.pqm --stderr=Microsoft-MIEngine-Error-ioyqbdlx.1lg --pid=Microsoft-MIEngine-Pid-hsh32dli.eyz "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
请输入数据
1 2 3 4 5 6 7 8 9 0
对象元素
1  2  3  4  5  6  7  8  9
相邻元素的和
1 + 2 = 3    2 + 3 = 5    3 + 4 = 7    4 + 5 = 9    5 + 6 = 11    6 + 7 = 13    7 + 8 = 15    8 + 9 = 17
头尾相加的和
1 + 9 = 10   2 + 8 = 10   3 + 7 = 10   4 + 6 = 10   5 + 5 = 10

E:\Users\86184\Documents\Code>

---------------------------------------------------------------------------------------------------------------------------------------------------
修正
#include <iostream>
#include <vector>
#include <cctype>
/*定义一个vector对象,读取一组数字,计算相邻元素的和,和头尾相加的和*/
int main(int argc, char const *argv[])
{
    using namespace std;
    int a = 1;
    bool flag = true;
    vector<int> veca;
    /*输入数据*/
    cout << "请输入数据" << endl;
    while (true)
    {
        cin >> a;
        /*舍去结束符0*/
        if (!a)
        {
            break;
        }
        veca.push_back(a);
    }
    decltype(veca.size()) i = 0, s = veca.size();
    /*判断是否非空集合*/
    if (veca.empty() || (s < 2))
    {
        cout << "请输入两个以上的非零数字";
        return 0;
    }
    /*查看向量的元素*/
    cout << "对象元素" << endl;
    for (auto a : veca)
    {
        cout << a << "  ";
    }
    cout << endl;
    /*计算相邻两个元素的和*/
    cout << "相邻元素的和" << endl;
    for (; i < s - 1; i++)
    {
        cout << veca[i] << " + " << veca[i + 1] << " = " << veca[i] + veca[i + 1] << "    ";
    }
    cout << endl;
    /*计算头尾相加的和*/
    cout << "头尾相加的和" << endl;
    for (i = 0; i < (s + 1) / 2; i++)
    {
        cout << veca[i] << " + " << veca[s - 1 - i] << " = " << veca[i] + veca[s - 1 - i] << "   ";
    }
    return 0;
}
***********************************************************************************
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-l1pit2dt.gjr --stdout=Microsoft-MIEngine-Out-k13clai2.sus --stderr=Microsoft-MIEngine-Error-0bil35nt.k1b --pid=Microsoft-MIEngine-Pid-344d1as5.uzi "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
请输入数据
a
请输入两个以上的非零数字

E:\Users\86184\Documents\Code>cmd /C "c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-pa1puxjz.vje --stdout=Microsoft-MIEngine-Out-hpyztzpx.p5p --stderr=Microsoft-MIEngine-Error-szvzjs0p.22y --pid=Microsoft-MIEngine-Pid-hh00x2dr.w1z "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi "
请输入数据
1 2 3 30 8545 878 a
对象元素
1  2  3  30  8545  878
相邻元素的和
1 + 2 = 3    2 + 3 = 5    3 + 30 = 33    30 + 8545 = 8575    8545 + 878 = 9423
头尾相加的和
1 + 878 = 879   2 + 8545 = 8547   3 + 30 = 33

E:\Users\86184\Documents\Code>

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-15 17:35:04 | 显示全部楼层
2019年8月15日17:35:00
迭代器
360截图20190815173356289.gif
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-17 12:34:08 | 显示全部楼层
本帖最后由 bin554385863 于 2019-8-17 21:23 编辑

2019年8月17日12:10:30
#include <iostream>
#include <vector>
#include <cctype>
#include <cmath>
/*判断字符串是不是一个整数*/
bool isnumber(std::string &strargs)
{
    bool flag = true;
    for (char cargs : strargs)
    {
        if (!isdigit(cargs))
        {
            flag = false;
            break;
        }
    }
    return flag;
}
/*将数字字符串转换成整数*/
std::vector<int> tonumber(std::string &strargs)
{
    std::vector<int> vecargs;
    const int size = strargs.size();
    double result = 0;
    for (size_t i = 0; i < size; i++)
    {
        result += (strargs[i] - 48) * pow(10, size - 1 - i);
    }
    vecargs.push_back(result);
    result = 0;
    return vecargs;
}
*****************************************************************************
#include <iostream>
#include <vector>
#include <cctype>
#include <cmath>
#include "E:\Users\86184\Documents\Code\Study\0_0_0_MyC++func.cpp"
/*使用迭代器使输入的数字翻倍输出*/
int main(int argc, char const * argv[])
{
        using namespace std;
        double ynum = 0;
        string wnum;
        vector<int> intnum;

        while (true)
        {
                cout << "请输入数字" << endl;

                cin >> wnum;
                if (!isnumber(wnum))
                {
                        cout << "输入含有其他字符,请重新输入" << endl;
                        continue;
                }
                intnum = tonumber(wnum);
                vector<int>::iterator it = intnum.begin();
                for (; it != intnum.end(); ++it)
                {
                        *it = *it * 2;
                }
                for (int num : intnum)
                {
                        cout << wnum << " X 2 = " << num << "  ";
                }
                cout<<endl;
        }

        return 0;
}
===================================================================
E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-0mcjprfn.ln5 --stdout=Microsoft-MIEngine-Out-aiq00ynt.j4f --stderr=Microsoft-MIEngine-Error-gsbuvl41.e2z --pid=Microsoft-MIEngine-Pid-e3rxfvlh.qsi "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
请输入数字
q
输入含有其他字符,请重新输入
请输入数字
+
输入含有其他字符,请重新输入
请输入数字
123 1000 3
123 X 2 = 246
请输入数字
1000 X 2 = 2000
请输入数字
3 X 2 = 6
请输入数字


E:\Users\86184\Documents\Code>

==================================================================
数组和向量
#include <iostream>
#include <string>
#include <vector>
/*使用数组初始化集合*/
int main(int argc, char const *argv[])
{
    using namespace std;
    int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 02, 35, 3, 54};
    /*使用数字初始化集合*/
    vector<int> vec(begin(arr), end(arr));//使用begin()和end()函数,数组名作为参数,获取数组的头指针和尾部指针
    vector<int> vecx(begin(arr) + 5, begin(arr) + 10);
    for (int i : vec)
    {
        cout << i << "  ";
    }
    cout << endl;
    for (int j : vecx)
    {
        cout << j << "  ";
    }
    return 0;
}
------------------------------------------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-kpilswng.fil --stdout=Microsoft-MIEngine-Out-lllfevgw.4pb --stderr=Microsoft-MIEngine-Error-f4342yjf.phl --pid=Microsoft-MIEngine-Pid-edkkjwrg.zlu "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
0  1  2  3  4  5  6  7  8  9  1  2  35  3  54
5  6  7  8  9

E:\Users\86184\Documents\Code>

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-20 01:07:30 | 显示全部楼层
本帖最后由 bin554385863 于 2019-8-20 01:20 编辑

2019年8月20日01:08:15
#include <iostream>
#include <initializer_list>
#include <vector>
/*可变参数类型std::initializer_list<type>*/
double sum(const std::initializer_list<int> &arg)
{
    double sum = 0;
    for(int num: arg)
    {
        sum += num;
    }
    return sum;
}
int main(int argc, char const *argv[])
{
    const int a = 10;
    const int b = 100, c = 63;
    /*std::initializer_list<int>参数列表元素全都是常量*/
    std::cout<<sum({1,-2,3,-4,5,-6,7,-8,9})<<std::endl;
    std::cout<<sum({a, b, c});
    return 0;
}
------------------------------------------------------------------------------------

E:\Users\86184\Documents\Code>cmd /C "c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.24.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-dpp1rvoj.vjf --stdout=Microsoft-MIEngine-Out-0knopx0m.s0l --stderr=Microsoft-MIEngine-Error-nrqlrhy2.j3p --pid=Microsoft-MIEngine-Pid-0nqtwlhr.btm "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi "
5
110

E:\Users\86184\Documents\Code>
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-24 01:45:16 | 显示全部楼层
2019年8月24日01:44:22
#include <iostream>
#include <string>
#include <vector>
#include<algorithm>
using namespace std;
/*编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""*/
string longestCommonPrefix(vector<string> &strs)
{
        string result;
        /*判定向量是否为空*/
        if (strs.empty())
        {
                result = {""};
        }
        else
        {
                size_t s = strs.size();
                if (s == 1)
                {
                        return strs[0];
                }
                else if (s > 1)
                {
                        /*构造向量字符串元素跳过对{"c", "c",...}和{"","",....}类似的向量元素的判定*/
                        strs[0] = strs[0] + "10lo";
                        strs[1] = strs[1] + "l01o";
                        /*初始化计数器*/
                        size_t count = 0;
                        /*定义数组,保存对比的计数结果*/
                        vector<int> arr;
                        for (size_t i = s - 1; i > 0; i--)
                        {
                                for (size_t j = 0;; j++)
                                {
                                        if (strs[0][j] != strs[i][j])
                                        {
                                                break;
                                        }
                                        count++;
                                }
                                arr.push_back(count);
                                /*重新初始化计数器*/
                                count = 0;
                        }
                        size_t sa = arr.size();
                        /*从小到大排序*/
                        sort(arr.begin(), arr.end());                
                        /*首位元素就是最终结果*/
                        if (arr[0] == 0)
                        {
                                result = {""};
                        }
                        else
                        {
                                for (size_t i = 0; i < arr[0]; i++)
                                {
                                        result.push_back(strs[1][i]);
                                }
                        }
                }
        }
        return result;
}
int main(int argc, char const *argv[])
{
        vector<string> a = {"boot", "boom", "boor", "boall"};
        vector<string> b = {"baac", "bacc", "b"};
        vector<string> c = {"flower"};
        vector<string> d = {"",""};
        vector<string> e =
                {
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
                };
        cout << longestCommonPrefix(a) << endl;
        cout << longestCommonPrefix(b) << endl;
        cout << longestCommonPrefix(c) << endl;
        cout << longestCommonPrefix(d) << endl;
        cout << longestCommonPrefix(e) << endl;
        return 0;
}
-------------------------------------------------------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.25.0\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-2uug3wdg.re0 --stdout=Microsoft-MIEngine-Out-fgrkj5jj.np0 --stderr=Microsoft-MIEngine-Error-hm0qrys1.x3k --pid=Microsoft-MIEngine-Pid-nxksjd20.xqb "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
bo
b
flower

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa


E:\Users\86184\Documents\Code>
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-25 20:12:39 | 显示全部楼层
2019年8月25日20:05:53
委托构造函数定义
function():func(0, 0,....){}
无参数构造函数 func()委托有参数构造函数funct(args, ....),构造对象

复制构造函数
classname(args,...  );---默认构造函数
classname(const classname &objectname);------复制构造函数
classname(const classname &objectname) = delete;-----禁止复制构造


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-28 01:25:49 | 显示全部楼层
2019年8月28日01:23:54
一个完整的类的定义
#include <iostream>
class clock
{
private:
    /*成员变量*/
    int Hour, Minute, Second;

public:
    /*有参数的构造函数*/
    clock(int hour, int minute, int second) : Hour(hour), Minute(minute), Second(second) {}
    /*默认构造函数委托有参数的构造函数初始化*/
    clock() : clock(0, 0, 0) {}
    /*复制构造函数*/
    clock(const clock &oarg);
    /*声明析构函数.当对象销毁时系统会自动调用析构函数做最后的清理*/
    ~clock();
    /*声明成员函数*/
    void setClock(int hour, int minute, int second);
    void showClock();
};
/*定义复制构造函数*/
clock::clock(const clock &oarg)
{
    Hour = oarg.Hour;
    Minute = oarg.Minute;
    Second = oarg.Second;
}
/*定义成员函数*/
void clock::setClock(int hour, int minute, int second)
{
    Hour = hour;
    Minute = minute;
    Second = second;
}
void clock::showClock()
{
    std::cout << Hour << ":" << Minute << ":" << Second << std::endl;
}
/*定义析构函数*/
clock::~clock()
{
    std::cout<<"调用析构函数"<<std::endl;
}
int main(int argc, char const *argv[])
{
    /*若没有定义有参数的构造函数,下面的语句就会报错*/
    clock a = {2, 3, 6};
    /*若自己没有定义无参数的构造函数,则编译器会自己添加一个默认的构造函数*/
    clock b;
    /*调用成员函数*/
    a.showClock();
    b.showClock();
    b.setClock(24, 20, 59);
    b.showClock();
    clock c = b;
    c.showClock();
    return 0;
}
-----------------------------------------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.25.0\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-iywbbnha.vug --stdout=Microsoft-MIEngine-Out-oyslqpxc.ymz --stderr=Microsoft-MIEngine-Error-nzoewxi5.lo3 --pid=Microsoft-MIEngine-Pid-kt3fdbr2.5b2 "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
a = 2:3:6
b = 0:0:0
重新设置b = 24:20:59
c = 24:20:59

调用析构函数
调用析构函数
调用析构函数


E:\Users\86184\Documents\Code>
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-8-29 21:01:46 | 显示全部楼层
2019年8月29日21:01:11
类的组合
#include <iostream>
#include <cmath>
class dot
{
private:
    int Y, X;

public:
    //构造
    dot(int x = 0, int y = 0) : X(x), Y(y) {}
    //复制构造
    dot(const dot &tdot) : X(tdot.X), Y(tdot.Y) {}
    int getX()
    {
        return X;
    }
    int getY()
    {
        return Y;
    }
    void setdot(int x, int y)
    {
        Y = y;
        X = x;
    }
    void showdot()
    {
        std::cout << "(" << X << " , " << Y << ")" << std::endl;
    }
};
class line
{
private:
    dot Start, End;
    double Length;
    

public:
    line(dot start, dot end) : Start(start), End(end)
    {
        double X = end.getX() - start.getX();
        double Y = end.getY() - start.getY();
        Length = sqrt(pow(X, 2) + pow(Y, 2));
    }
    line(const line &tline) : Start(tline.Start), End(tline.End) 
    {
        Length = tline.Length;
    }
    void length()
    {
        std::cout << Length << std::endl;
    }
};
int main(int argc, char const *argv[])
{
    dot start = {5, 9}, end = {9, 20};
    line aline = {start, end};
    aline.length();
    line kline = aline;
    kline.length();
    return 0;
}
---------------------------------------------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.25.0\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-ofgnd5qn.5eu --stdout=Microsoft-MIEngine-Out-2zgryx00.0rd --stderr=Microsoft-MIEngine-Error-rvusglds.1ia --pid=Microsoft-MIEngine-Pid-ww5hv3ry.sbq "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
11.7047
11.7047


E:\Users\86184\Documents\Code>
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-4 00:09:42 | 显示全部楼层
本帖最后由 bin554385863 于 2019-9-4 00:14 编辑

2019年9月4日00:09:00
类的继承
#include <iostream>
/*父类*/
class shape
{
protected:
    unsigned int H, L;

public:
    int area()
    {
        return H * L;
    }
};
//子类
class rect : public shape
{
public:
    rect(int l = 0, int h = 0)
    {
        L = l;
        H = h;
    }
    rect(const rect &r)
    {
        L = r.L;
        H = r.H;
    }
    ~rect()
    {
        std::cout << "子类不能使用初值列初始化构造函数" << std::endl;
    };
};
int main(int argc, char const *argv[])
{
    rect r1;
    std::cout <<"r1.area = "<< r1.area() << std::endl;
    rect r2 = {9, 6};
    std::cout <<"r2.area = "<< r2.area() << std::endl;
    return 0;
}
-----------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.25.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-eq2d2nqk.0np --stdout=Microsoft-MIEngine-Out-i5vlflfq.qos --stderr=Microsoft-MIEngine-Error-yp3hcvsu.fap --pid=Microsoft-MIEngine-Pid-csr0yko2.w4f "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
r1.area = 0
r2.area = 54
子类不能使用初值列初始化构造函数
子类不能使用初值列初始化构造函数


E:\Users\86184\Documents\Code>

-----------------------------------------------------------------------------------------------------
---
子类拥有父类的一切非私有数据和函数

---
公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。

---
更正:
子类不能使用父类的数据成员用作初值列初始化子类构造函数
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-4 23:46:51 | 显示全部楼层
2019年9月4日23:42:54
运算符重载
#include <iostream>
#include <cmath>
class sinx_y
{
private:
    double X;

public:
    sinx_y(double x = 0) : X(x) {}
    sinx_y(const sinx_y &s) : X(s.X) {}
    sinx_y operator+(const sinx_y &y)
    {
        sinx_y result;
        result.X = this->X * sqrt(1 - y.X * y.X) + sqrt(1 - this->X * this->X) * y.X;
        return result;
    }
    sinx_y operator-(const sinx_y &y)
    {
        sinx_y result;
        result.X = this->X * sqrt(1 - y.X * y.X) - sqrt(1 - this->X * this->X) * y.X;
        return result;
    }
    double showresult()
    {
        return X;
    }
};
int main(int argc, char const *argv[])
{
    sinx_y x = 0.5, y = sqrt(3.0) / 2;
    sinx_y z = x + y;
    std::cout << "sin(x+y) = " << z.showresult() << std::endl;
    sinx_y k = y - x;
    std::cout << "sin(x-y) = " << k.showresult() << std::endl;
    return 0;
}
-----------------------------------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.25.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-jug5q2wo.xhj --stdout=Microsoft-MIEngine-Out-3r2wfnyi.ee5 --stderr=Microsoft-MIEngine-Error-obkgmcpp.fg3 --pid=Microsoft-MIEngine-Pid-yyvklzxr.whs "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
sin(x+y) = 1
sin(x-y) = 0.5


E:\Users\86184\Documents\Code>

--------------------------------------------------------------------------------------
下面是可重载的运算符列表:

+        -        *        /        %        ^
&        |        ~        !        ,        =
<        >        <=        >=        ++        --
<<        >>        ==        !=        &&        ||
+=        -=        /=        %=        ^=        &=
|=        *=        <<=        >>=        []        ()
->        ->*        new        new []        delete        delete []
下面是不可重载的运算符列表:

::        .*        .        ?:

重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-8 23:35:48 | 显示全部楼层
2019年9月8日23:37:20
虚函数-继承-多态

关注问题:

虚函数的作用
虚函数的实现原理
虚函数表在对象布局里的位置
虚函数的类的sizeof
纯虚函数的作用
多级继承时的虚函数表内容
虚函数如何执行父类代码
多继承时的虚函数表定位,以及对象布局
虚析构函数的作用
虚函数在QT的信号与槽中的应用
虚函数与inline修饰符,static修饰符
前面我们尝试了一个简单的例子,接下来尝试一个多级继承的例子,以及一个多继承的例子。主要涉及到以下问题:多级继承时虚函数表的内容是如何填写的,如何在多级继承的情况下调用某一级父类里的虚函数,以及在多继承(多个父类)的情况下的对象布局。

多级继承
在这里,多级继承指的是有3层或者多层继承关系的情形。让我们看看下面的代码:

多层继承代码示例

//Source filename: Win32Con.cpp
#include <iostream>
using namespace std;
class parent1
{
public:
    virtual int fun1(){cout<<"parent1::fun1()"<<endl;return 0;};
    virtual int fun2()=0;
};
class child1:public parent1
{
public:
    virtual int fun1()
    {
        cout<<"child1::fun1()"<<endl;
        parent1::fun1();
        return 0;
    }
    virtual int fun2()
    {
        cout<<"child1::fun2()"<<endl;
        return 0;
    }
};
class grandson:public child1
{
public:
    virtual int fun2()
    {
        cout<<"grandson::fun2()"<<endl;
        //parent1::fun2();
        parent1::fun1();
        child1::fun2();
        return 0;
    }
};
void test_func1(parent1 *pp)
{
    pp->fun1();
    pp->fun2();
}
int main(int argc, char* argv[])
{
    grandson sunzi;
    test_func1(&sunzi);
    return 0;
}
这段代码展示了三个class,分别是parent1,child1,grandson。

类parent1定义了两个虚函数,其中fun2是一个纯虚函数,这个类是一个不可实例化的抽象基类。
类child1继承了parent1,并且对两个虚函数fun1和fun2都编写了实现的代码,这个类可以被实例化。
类grandson继承了child1,但是只对虚函数fun2编写了实现的代码。
此外,我们还改写了test_func1函数,它的参数为parent1类型的指针,我们可以将parent1的子孙类作为这个函数的参数传入。在这个函数里,我们将依次调用parent1类的两个虚函数。

可以先通过阅读代码预测一下程序的输出内容。

程序的输出内容将是:

child1::fun1()
parent1::fun1()
grandson::fun2()
parent1::fun1()
child1::fun2()
先看第一行输出child1::fun1(),为什么会输出它呢?我们定义的具体对象sunzi是grandson类型的,test_func1的参数类型是parent1类型。在调用这个虚函数的时候,是完成了一次怎样的调用过程呢?

让我们再次使用cl命令输出这几个类的对象布局:

class parent1   size(4):
        +---
0      | {vfptr}
        +---

parent1::$vftable@:
        | &parent1_meta
        |  0
0      | &parent1::fun1
1      | &parent1::fun2

parent1::fun1 this adjustor: 0
parent1::fun2 this adjustor: 0

class child1    size(4):
        +---
        | +--- (base class parent1)
0      | | {vfptr}
        | +---
        +---

child1::$vftable@:
        | &child1_meta
        |  0
0      | &child1::fun1
1      | &child1::fun2

child1::fun1 this adjustor: 0
child1::fun2 this adjustor: 0

class grandson  size(4): //grandson的对象布局
        +---
        | +--- (base class child1)
        | | +--- (base class parent1)
0      | | | {vfptr}
        | | +---
        | +---
        +---

grandson::$vftable@:  //grandson虚函数表的内容
        | &grandson_meta
        |  0
0      | &child1::fun1
1      | &grandson::fun2

grandson::fun2 this adjustor: 0
因为我们实例化的是一个grandson对象,让我们看看它的对象布局。正如前面的例子一样,里面只有一个vfptr指针,但是不一样的却是这个指针所指的虚函数表的内容:

第一个虚函数,填写的是child1类的fun1的地址;第二个虚函数填写的才是grandson类的fun2的地址。

很显然我们可以得出这样一个结论:在一个子对象的虚函数表里,每一个虚函数的实际运行的函数地址,将填写为在继承体系里最后实现该虚函数的函数地址。

所以当我们在test_func1里调用了传入的parent1指针的fun1函数的时候,我们实际执行的是填写在虚函数表里的 child1::fun1(),而调用fun2函数的时候,是从虚函数表里得到了grandson::fun2函数的地址并调用之。在“程序输出结果”表里的第一行和第三行结果证实了上述结论。

再看一下程序代码部分的child1::fun1()的实现代码,在第18行,我们有parent1::fun1();这样的语句,这行代码输出了运行结果里的第二行,而在grandson::fun2()的实现代码第35行的parent1::fun1();以及第36行的child1::fun2();则输出了运行结果里的第四行和第五行的内容。这三行代码展示了如何调用父类以及更高的祖先类里的虚函数。——事实上,这与调用父类的普通函数没有任何区别。

在程序代码的第34行,有一行被注释了的内容//parent1::fun2();,之所以会注释掉,是因为这样的代码是无法通过编译的,因为在parent1类里,fun2是一个“纯虚函数”也就是说这个函数没有代码实体,在编译的时候,链接器将无法找到fun2的目标代码从而报错。

其实有了对虚函数的正确的认识,上面的多级继承是很自然就能明白的。然而在多继承的情况下,情况就有所不同了。。。

多继承下虚函数的使用
假如一个类,它由多个父类继承而来,而在不同的父类的继承体系里,都存在虚函数的时候,这个类的对象布局又会是怎样的?它又是怎样定位虚函数的呢?

让我们看看下面的代码:

程序输出结果

//Source filename: Win32Con.cpp
#include <iostream>
using namespace std;
class parent1
{
public:
    virtual int fun1(){cout<<"parent1::fun1()"<<endl;return 0;};
};
class parent2
{
public:
    virtual int fun2(){cout<<"parent2::fun2()"<<endl;return 0;};
};
class child1:public parent1,public parent2
{
public:
    virtual int fun1()
    {
        cout<<"child1::fun1()"<<endl;
        return 0;
    }
    virtual int fun2()
    {
        cout<<"child1::fun2()"<<endl;
        return 0;
    }
};
void test_func1(parent1 *pp)
{
    pp->fun1();
}
void test_func2(parent2 *pp)
{
    pp->fun2();
}
int main(int argc, char* argv[])
{
    child1 chobj;
    test_func1(&chobj);
    test_func2(&chobj);
    return 0;
}
这一次,我们有两个父类,parent1和parent2,在parent1里定义了虚函数fun1,而在parent2里定义了虚函数fun2,然后我们有一个子类child1,在里面重新实现了fun1和 fun2两个虚函数。然后我们编写了test_func1函数来调用parent1类型对象的fun1函数,编写了test_func2函数调用 parent2对象的fun2函数。在main函数里我们实例化了一个child1类型的对象chobj,然后分别传给test_func1和 test_func2去执行。

这段代码的运行结果非常简单就能看出来:

child1::fun1()

child1::fun2()

但是,让我们看看对象布局吧:

class child1    size(8):
        +---
        | +--- (base class parent1)
0      | | {vfptr}
        | +---
        | +--- (base class parent2)
4      | | {vfptr}
        | +---
        +---

child1::$vftable@parent1@:
        | &child1_meta
        |  0
0      | &child1::fun1

child1::$vftable@parent2@:
        | -4
0      | &child1::fun2

child1::fun1 this adjustor: 0
child1::fun2 this adjustor: 4
注意到没?在child1的对象布局里,出现了两个vfptr指针!

这两个虚函数表指针分别继承于parent1和parent2类,分别指向了不同的两个虚函数表。

问题来了,当我们使用test_func1调用parent1类的fun1函数的时候,调用个过程还比较好理解,可以从传入的地址参数取得继承自 parent1的vfptr,从而执行正确的fun1函数代码,但是当我们调用test_func2函数的时候,为什么程序可以自动取得来自 parent2的vfptr呢,从而得出正确的fun2函数的地址呢?

其实,这个工作是编译器自动根据实例的类型完成的,在编译阶段就已经确定了在调用test_func2的时候,传入的this指针需要增加一定的偏移(在这里则是第一个vfptr所占用的大小,也就是4字节)。

我们可以看看main函数里这部分代码的反汇编代码:

反汇编代码

    child1 chobj;
00F5162E 8D 4D F4         lea         ecx,[chobj]
00F51631 E8 F5 FB FF FF   call        child1::child1 (0F5122Bh)
    test_func1(&chobj);
00F51636 8D 45 F4         lea         eax,[chobj]
00F51639 50               push        eax  
00F5163A E8 6F FB FF FF   call        test_func1 (0F511AEh)
00F5163F 83 C4 04         add         esp,4
    test_func2(&chobj);
00F51642 8D 45 F4         lea         eax,[chobj]
00F51645 85 C0            test        eax,eax
00F51647 74 0E            je          main+47h (0F51657h)
00F51649 8D 4D F4         lea         ecx,[chobj]
00F5164C 83 C1 04         add         ecx,4
00F5164F 89 8D 2C FF FF FF mov         dword ptr [ebp-0D4h],ecx
00F51655 EB 0A            jmp         main+51h (0F51661h)
00F51657 C7 85 2C FF FF FF 00 00 00 00 mov         dword ptr [ebp-0D4h],0
00F51661 8B 95 2C FF FF FF mov         edx,dword ptr [ebp-0D4h]
00F51667 52               push        edx  
00F51668 E8 F6 FA FF FF   call        test_func2 (0F51163h)
00F5166D 83 C4 04         add         esp,4
    return 0;
从第4行至第5行,执行的是test_func1函数,this指针指向 chobj (第2行lea ecx,[chobj]),但是调用test_func2函数的时候,this指针被增加了4(第14行)!于是,在test_func2执行的时候,就可以从&chobj+4的地方获得vfptr指针,从而根据parent2的对象布局得到了fun2的地址并执行了。

为了证实这点,我们可以将代码做如下的修改:

1:  int main(int argc, char* argv[])
2:  {
3:      child1 chobj;
4:      test_func1(&chobj);
5:      test_func2((parent2 *)(void *)&chobj);
6:      return 0;
7:  }
8:  
请注意红色部分的变化,在讲chobj传入给test_func2之前,先用(void *)强制转换为无类型指针,再转换为parent2 指针,这样的转换,显然是可行的,因为chobj本身就是parent2的子类,然而,程序的执行效果却是:

child1::fun1()

child1::fun1()

执行test_func2函数,调用的是parent2::fun2,但是居然执行的是child1::fun1()函数!!!

这中间发生了些什么呢?我们再看看反汇编的代码:

反汇编的代码

    child1 chobj;
013D162E 8D 4D F4         lea         ecx,[chobj]
013D1631 E8 F5 FB FF FF   call        child1::child1 (13D122Bh)
    test_func1(&chobj);
013D1636 8D 45 F4         lea         eax,[chobj]
013D1639 50               push        eax  
013D163A E8 6F FB FF FF   call        test_func1 (13D11AEh)
013D163F 83 C4 04         add         esp,4
    test_func2((parent2*)(void *)&chobj);
013D1642 8D 45 F4         lea         eax,[chobj]
013D1645 50               push        eax  
013D1646 E8 18 FB FF FF   call        test_func2 (13D1163h)
013D164B 83 C4 04         add         esp,4
    return 0;
从调用test_func2的反汇编代码可以看到,这一次ecx寄存器的值没有做改变!所以在执行test_func2的时候,将取得 parent1对象布局里的vfptr,而这个vfptr所指的虚函数表里的第一项就是fun1,并且被填写为child1::fun1的地址了。所以才出现了child::fun1的输出内容!显然这里有一个隐藏的致命问题,加入parent1和parent2的第一个虚函数的参数列表不一致,这样的调用显然就会导致堆栈被破坏掉,程序99%会立即崩溃。之前的程序没有崩溃并且成功输出内容,不过是因为parent1::fun1()和 parent2::fun2()的参数列表一致的关系而已。
所以,千万不要在使用一个多继承对象的时候,将其类型信息丢弃,编译器还需要依靠正确的类型信息,在使用虚函数的时候来得到正确的汇编代码!

多继承与虚函数重复
既然说到了多继承,那么还有一个问题可能会需要解决,那就是如果两个父类里都有相同的虚函数定义,在子对象的布局里会是怎么样个情况?是否依然可以将这个虚函数指向到正确的实现代码上呢?

修改前面一个源代码,在parent2的接口里增加下面的虚函数定义:

virtual int fun1(){cout<<"parent2::fun1()"<<endl;return 0;};
上面的fun1的定义与parent1类里的完全重复相同(类型,参数列表),增加上面的代码后立即开始编译,程序正常编译通过。运行之,得到下面的结果:

child1::fun1()

child1::fun2()

这个程序居然正确的完成了执行,编译器在其中做了些怎样的工作,是怎么样避免掉队fun1函数的冲突问题呢?

让我们来看看这个时候的child1的对象布局:

class child1    size(8):
        +---
        | +--- (base class parent1)
0      | | {vfptr}
        | +---
        | +--- (base class parent2)
4      | | {vfptr}
        | +---
        +---

child1::$vftable@parent1@:
        | &child1_meta
        |  0
0      | &child1::fun1

child1::$vftable@parent2@:
        | -4
0      | &child1::fun2
1      | &thunk: this-=4; goto child1::fun1

child1::fun1 this adjustor: 0
child1::fun2 this adjustor: 4
恩~~~还是两个vfptr在child1的对象布局里(不一样就怪啦,呵呵),但是第二个vfptr所指的虚函数表的内容有所变化哦!

注意看红色字体部分,虚函数表里并没有直接填写child::fun1的代码,而是多了一个 &thunk: this-=4;然后才goto child1::fun1!注意到一个关键名词thunk了吧?没错,vc在这里使用了名为thunk的技术,避免了虚函数fun1在两个基类里重复出现导致的冲突问题!(除了thunk,还有其他方法可以解决此类问题的)。

现在,我们知道为什么相同的虚函数不会在子类里出现冲突的情况了。

但是,倘若我们在基类里就是由两个冲突的普通函数,而不是虚函数,是个怎样的情况呢?

多继承产生的冲突与虚继承,虚基类
我们在parent1和parent2里添加一个相同的函数void fun3(),然后再进行编译,通过了!查看类对象布局,跟上面的完全一致。但是在main函数里调用chobj.fun3()的时候,编译器却不再能正确编译了,并且会提示“error C2385: 对“fun3”的访问不明确”的错误信息,没错,编译器不知道你要访问哪个fun3了。

如何解决这样的多继承带来的问题呢,其实有一个简单的做法。就是在方法前限定引用的具体是哪个类的函数,比如:chobj.parent1::fun3();  ,这样的写法就写明了是要调用chobj的父类parent1里的fun3()函数!

我们再看看另外一种情况,从parent1和parent2里抹去刚才添加的fun3函数,将之放到一个共同的基类里:

class commonbase
{
public:
    void fun3(){cout<<"commonbase::fun3()"<<endl;}
};
而parent1和parent2都修改为从此类继承。可以看到,在这个情况下,依然需要使用chobj.parent1::fun3();  的方式才可以正确调用到fun3,难道,在这种情况下,就不能自然的使用chobj.fun3()这样的方式了吗?

虚继承可以解决这个问题——我们在parent1和parent2继承common类的地方添加上一个关键词virtual,如下:

class parent1:virtual public commonbase
{
public:
    virtual int fun1(){cout<<"parent1::fun1()"<<endl;return 0;};
};
给parent2也同样的处理,然后再次编译,这次chobj.fun3()可以编译通过了!!!

编译器这次又在私下里做了哪些工作了呢????

class child1    size(16):
        +---
        | +--- (base class parent1)
0      | | {vfptr}
4      | | {vbptr}
        | +---
        | +--- (base class parent2)
8      | | {vfptr}
12      | | {vbptr}
        | +---
        +---
        +--- (virtual base commonbase)
        +---

child1::$vftable@parent1@:
        | &child1_meta
        |  0
0      | &child1::fun1

child1::$vftable@parent2@:
        | -8
0      | &child1::fun2
1      | &thunk: this-=8; goto child1::fun1

child1::$vbtable@parent1@:
0      | -4
1      | 12 (child1d(parent1+4)commonbase)

child1::$vbtable@parent2@:
0      | -4
1      | 4 (child1d(parent2+4)commonbase)

child1::fun1 this adjustor: 0
child1::fun2 this adjustor: 8

vbi:       class  offset o.vbptr  o.vbte fVtorDisp
      commonbase      16       4       4 0
这次变化可大了去了!!!

首先,可以看到两个类parent1和parent2的对象布局里,都多了一个vbptr的指针。而在child1的对象布局里,还有一个virtual base commonbase的虚拟基类。再看看两个vbptr的内容:

12 (child1d(parent1+4)commonbase) 这个很好理解,从parent1的vbptr开始,偏移12个字节,指向的是virtual base commonbase!

再看看4 (child1d(parent2+4)commonbase) ,从parent2的vbptr开始,便宜4个字节,也指向了virtual base commonbase!

这下明白了。虚基类在child1里只有一个共同的对象布局了,所以就可以直接用chobj.fun3()啦,当然,在commonbase里的其他成员变量此时也可以同样的方式访问了!

虽然解决方案有了,但是在一个系统的设计里,如果有一个基类出现多继承冲突的情况,大部分情况下都说明这样的设计是有问题的,应该尽量避免这样的设计,并且尽量用纯虚函数,来提取一些抽象的接口类,把共同的方法接口都抽取出来,通常就能避免多继承的问题。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-10 18:49:34 | 显示全部楼层
本帖最后由 bin554385863 于 2019-9-10 22:51 编辑

2019年9月10日18:43:35
.组合
类的成员中出现其他类的对象,叫做类的组合.

.继承

.虚继承:
父类:A;
子类B , C继承于A;
子类D继承于B, C;
则B, C, 需要使用虚继承,避免D中出现重复的A.

class A {...}
class B: virtual public A {...}
class C: virtual public A {...}
class D: public B, public C {...}


.虚函数
当多个子类需要改写父类的成员函数时, 父类中对应的成员函数需要声明为虚函数

.虚函数的特点
关于虚函数的特点:
(1)要有子类公有继承父类,虚函数才有意义
(2)子类重写的父类的虚函数也是虚函数(类Dog中的eat函数是虚函数),只不过省略了关键字virtual
(3)虚函数必须是所在类的成员函数,而不能是友元函数或静态函数。因为虚函数调用要靠特定的对象来决定激活哪一个函数。
(4)构造函数不能是虚函数,原因是构造函数在创建时首先被调用,也就是说在编译时就要把构造函数的地址绑定好。而析构函数可以是虚函数,因为析构函数最后调用,可以在运行时动态匹配。

基类定义了虚函数, 并且是public的,那么子类只要override 虚函数 无论放在什么样的访问权限下(private,protect,public), 都以基类的访问权限为准, 即是public的。


.虚析构
把析构函数设为虚函数主要是为了防止内存泄漏
我们首先要知道的事实(单继承时):
构造函数的调用顺序:从当前类往上找父类,一直找到最上层的父类,我们可以理解为始祖,它是最先构造的,然后沿着继承路径依次往下构造,一直到当前类。
析构函数的调用顺序:从当前类开始析构,析构完再沿着继承路径往上找父类 ,析构父类 ,再找到最上层的父类 析构

.虚函数的默认参数
虚函数是动态绑定的,而虚函数的默认参数是静态绑定的,因此在编译时已经把虚函数的参数的值确定了,而不管在后面执行时动态调用哪个虚函数改变的值
#include <iostream>
using namespace std;

class A
{
public:
    A()
    {
        cout << "A的构造函数" << endl;
    }
    ~A()
    {
        cout << "A的析构函数" << endl;
    }
};

class B:public A
{
public:
    B()
    {
        p = new char[20];
        cout << "B的构造函数" << endl;
    }
    ~B()
    {
        if (p != NULL)
            delete[] p;
        cout << "B的析构函数" << endl;
    }

private:
    char *p;
};

void func(A *pa)
{
    delete pa;
}


int main()
{
    A *pa = new B;      //构造函数被调用两次,A先B后
    func(pa);           //析构函数只被调用一次,A调用

    return 0;
}
----------------------------------------------------------------------
————————————————
版权声明:本文为CSDN博主「qq_1061856980華仔」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hua12134/article/details/79780419

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-9-11 00:55:38 | 显示全部楼层
本帖最后由 bin554385863 于 2019-9-11 11:27 编辑

2019年9月11日00:55:53
虚函数,虚继承
#include <iostream>
class animal
{
public:
    animal()
    {
        std::cout << "父类构造" << std::endl;
    }
    void eat()
    {
        std::cout << "父类成员函数eat()" << std::endl;
    }
    virtual void sleep()
    {
        std::cout << "父类成员函数sleep()" << std::endl;
    }
    virtual ~animal()
    {
        std::cout << "父类析构函数" << std::endl;
    }
};
class dog : virtual public animal
{
public:
    dog()
    {
        std::cout << "子类dog构造函数" << std::endl;
    }
    virtual void sleep()
    {
        std::cout << "子类dog成员函数sleep()" << std::endl;
    }
    virtual ~dog()
    {
        std::cout << "子类dog析构函数" << std::endl;
    }
};
class pig : virtual public animal
{
public:
    pig()
    {
        std::cout << "子类pig构造函数" << std::endl;
    }
    virtual void sleep()
    {
        std::cout << "子类pig成员函数sleep()" << std::endl;
    }
    virtual ~pig()
    {
        std::cout << "子类pig析构函数" << std::endl;
    }
};
class ppg : public dog, public pig
{
public:
    ppg()
    {
        std::cout << "ppg构造函数" << std::endl;
    }
    virtual void sleep()
    {
        std::cout << "ppg成员函数sleep()" << std::endl;
    }
    virtual ~ppg()
    {
        std::cout << "ppg析构函数" << std::endl;
    }
};
int main(int argc, char const *argv[])
{
    ppg p;
    animal *ppg = &p;
    ppg->sleep();
    ppg->eat();
    return 0;
}
---------------------------------------------------------------------------------------------------------------
Microsoft Windows [版本 10.0.16299.1087]
(c) 2017 Microsoft Corporation。保留所有权利。

E:\Users\86184\Documents\Code>c:\Users\86184\.vscode\extensions\ms-vscode.cpptools-0.25.1\debugAdapters\bin\WindowsDebugLauncher.exe --stdin=Microsoft-MIEngine-In-2i0pz4rl.ub2 --stdout=Microsoft-MIEngine-Out-1z1pnj4a.tq0 --stderr=Microsoft-MIEngine-Error-5ykprrfo.1a2 --pid=Microsoft-MIEngine-Pid-ievsqwkx.vhs "--dbgExe=E:\My Program\MinGW\bin\gdb.exe" --interpreter=mi
父类构造
子类dog构造函数
子类pig构造函数
ppg构造函数

ppg成员函数sleep()
父类成员函数eat()

ppg析构函数
子类pig析构函数
子类dog析构函数
父类析构函数


E:\Users\86184\Documents\Code>
-----------------------------------------------------------------------
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-21 01:27

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表