鱼C论坛

 找回密码
 立即注册
查看: 1423|回复: 7

[技术交流] C++ 自定义numbers类 初步完成

[复制链接]
发表于 2019-11-21 23:45:38 | 显示全部楼层 |阅读模式

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

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

x
numbers.txt
构造函数:
//默认构造
numbers():numsLen(0), numsPtr(nullptr){}
//使用C风格数字数组构造
numbers(const tp *arr, const size_t arrSize);
//可变参数构造
numbers(std::initializer_list<tp> list);
//拷贝构造
numbers(const numbers<tp>& nums);
****************************************************
成员函数

at():
按索引获取数字串的元素;
按索引修改元素的值;

size():
获取对象的大小
****************************************************
重载运算符

[](改)
安索引修改对象元素的值;

+(增):
添加元素;
合并对象;

-(删)
删除对象中指定的数据;
删除父对象中与子对象相同的数据;
按索引删除元素参数必须显示声明为size_t类型

%(查)
判断对象是否包含某数据;
判断子对象是否父对象的子串;

/(查):
返回数据在对象中出现的次数;
返回对象与对象相同的元素;

赋值运算符:

+= 
/=
-=
右边必须是可改变的对象;


numbers.h
#ifndef NUMBERS_H
#define NUMBERS_H
#include <iostream>
#include <initializer_list>
#include <string>
template <typename tp>
class numbers
{
private:
    size_t numsLen;
    tp *numsPtr;
public:
    //1--默认构造
    numbers() : numsLen(0), numsPtr(nullptr) {}
    //2--使用C风格数字数组构造
    numbers(const tp *arr, const size_t arrSize);
    //3--可变参数构造
    numbers(std::initializer_list<tp> list);
    //4--拷贝构造
    numbers(const numbers<tp> &nums);
    /*--------------------------------------------------------*/
    //5--按索引获取元素;按索引修改元素的值
    tp &at(const size_t index) const;
    //6--对象的大小
    size_t const size() const;
    /*--------------------------------------------------------*/
    //7--重载下标运算符
    tp &operator[](const size_t index) const;
    //8--深度拷贝
    numbers<tp> &operator=(const numbers<tp> &nums);
    //9--重载流运算符<<
    friend std::ostream &operator<<(std::ostream &os, const numbers<tp> &nums)
    {
        size_t sz = nums.size();
        for (size_t i = 0; i < sz; i++)
        {
            os << nums[i] << " ";
        }
        return os;
    }
    /*--------------------------------------------------------*/
    //10--添加元素
    numbers<tp> operator+(const tp num) const;
    numbers<tp>& operator+=(const tp num)
    {
        *this = *this + num; 
        return *this;
    }
    //11--合并对象
    numbers<tp> operator+(const numbers<tp> &nums) const;
    numbers<tp>& operator+=(const numbers<tp> &nums)
    {
        *this = *this + nums; 
        return *this;
    }
    /*--------------------------------------------------------*/
    //12--判断数据在对象中存在
    bool operator%(const tp num) const;
    //13--判断子对象是否父对象的子串
    bool operator%(const numbers<tp>& nums) const;
    /*--------------------------------------------------------*/
    //14--返回数据出现的次数
    size_t operator/(tp num) const;
    //15--返回两个对象共同的元素
    numbers<tp> operator/(const numbers<tp> &nums) const;
    //15-1返回并赋值
    numbers<tp> operator/=(const numbers<tp> &nums)
    {
        *this = *this / nums;
        return *this;
    }
    /*--------------------------------------------------------*/
    //16--删除元素
    numbers<tp> operator-(const tp num) const;
    numbers<tp> operator-=(const tp num)
    {
        *this = *this - num;
        return *this;
    }
    //17--删除父对象中与子对象中重复的元素
    numbers<tp> operator-(const numbers<tp> &nums) const;
    numbers<tp> operator-=(const numbers<tp> &nums)
    {
        *this = *this - nums;
        return *this;
    }
    //18--按索引删除元素(数据必须显示声明size_t类型)失败返回空串
    numbers<tp> operator-(const size_t index) const;
    numbers<tp> operator-=(const size_t index)
    {
        *this = *this - this->at(index);
        return *this;
    }
    /*--------------------------------------------------------*/
    ~numbers()
    {
        delete[] numsPtr;
    }
};

#endif



numbers.cpp
#include "numbers.h"
//2--使用C风格数字数组构造
template <typename tp>
numbers<tp>::numbers(const tp *arr, const size_t arrSize) : numsLen(arrSize)
{
    numsPtr = new tp[numsLen];
    for (size_t i = 0; i < numsLen; i++)
    {
        *(numsPtr + i) = *(arr + i);
    }
}
//3--可变参数构造
template <typename tp>
numbers<tp>::numbers(std::initializer_list<tp> list) : numsLen(list.size())
{
    numsPtr = new tp[numsLen];
    for (size_t i = 0; i < numsLen; i++)
    {
        *(numsPtr + i) = *(list.begin() + i);
    }
}
//4--拷贝构造
template <typename tp>
numbers<tp>::numbers(const numbers<tp> &nums)
{
    if (nums.numsPtr != nullptr)
    {
        numsLen = nums.numsLen;
        numsPtr = new tp[numsLen];
        for (size_t i = 0; i < numsLen; i++)
        {
            *(numsPtr + i) = *(nums.numsPtr + i);
        }
    }
    else
    {
        numsLen = 0;
        numsPtr = nullptr;
    }
    
}
/*--------------------------------------------------------------------*/
//5--按索引获取元素;按索引修改元素的值
template <typename tp>
inline tp &numbers<tp>::at(const size_t index) const
{
    return numsPtr[index];
}
//6--对象的大小
template <typename tp>
inline const size_t numbers<tp>::size() const
{
    return numsLen;
}
/*--------------------------------------------------------*/
//7--重载下标运算符
template <typename tp>
inline tp &numbers<tp>::operator[](const size_t index) const
{
    return numsPtr[index];
}
/*--------------------------------------------------------*/
//8--深度拷贝
template <typename tp>
numbers<tp> &numbers<tp>::operator=(const numbers<tp> &nums)
{
    if ((numsPtr != nullptr)&&(nums.numsPtr != nullptr))//非空串的深度拷贝
    {
        numsLen = nums.numsLen;
        delete[] numsPtr;
        numsPtr = new tp[numsLen];
        for (size_t i = 0; i < numsLen; i++)
        {
            *(numsPtr + i) = *(nums.numsPtr + i);
        }
    }
    if((numsPtr == nullptr)&&(nums.numsPtr != nullptr))//调用对象为空,操作对象非空
    {
        numsLen = nums.numsLen;
        numsPtr = new tp[numsLen];
        for (size_t i = 0; i < nums.numsLen; i++)
        {
            this->at(i) = *(nums.numsPtr + i);
        }
    }
    if ((numsPtr != nullptr)&&(nums.numsPtr == nullptr))//调用对象非空,操作对象为空
    {
        numsLen = 0;
        delete [] numsPtr;
        numsPtr = nullptr;
    }
    
    return *this;
}
/*--------------------------------------------------------*/
//10--添加元素
template <typename tp>
numbers<tp> numbers<tp>::operator+(const tp num) const
{
    numbers<tp> r_nums;
    if (numsPtr != nullptr)
    {
        r_nums = *this;
        tp *t_numsPtr = new tp[numsLen + 1];
        for (size_t i = 0; i < numsLen; i++)
        {
            *(t_numsPtr + i) = this->at(i);
        }
        *(t_numsPtr + numsLen) = num;
        r_nums.numsLen++;
        delete[] r_nums.numsPtr;
        r_nums.numsPtr = t_numsPtr;
    }
    else
    {
        r_nums.numsLen = 1 + numsLen;
        r_nums.numsPtr = new tp[numsLen + 1]{num};
    }

    return r_nums;
}
//11--合并对象
template <typename tp>
numbers<tp> numbers<tp>::operator+(const numbers<tp> &nums) const
{
    numbers<tp> t_nums;
    if (numsPtr != nullptr)
    {
        t_nums = *this;
        for (size_t i = 0; i < nums.numsLen; i++)
        {
            t_nums = t_nums + *(nums.numsPtr + i);
        }
    }
    else
    {
        t_nums = nums;
    }

    return t_nums;
}
/*--------------------------------------------------------*/
//12--判断数据在对象中是否存在
template <typename tp>
bool numbers<tp>::operator%(const tp num) const
{
    bool flag = false;
    if (numsPtr != nullptr)
    {
        for (size_t i = 0; i < numsLen; i++)
        {
            if (num == *(numsPtr + i))
            {
                flag = true;
                break;
            }
        }
    }
    return flag;
}
//13--判断子对象是否父对象的子串
template <typename tp>
bool numbers<tp>::operator%(const numbers<tp> &nums) const
{
    bool flag = false;
    size_t count = 1;
    if (numsLen > nums.numsLen)
    {
        for (size_t i = 0; i < nums.numsLen; i++)
        {
            if ((*this) % (*(nums.numsPtr + i)))
            {
                count++;
                if (count == nums.numsLen)
                {
                    break;
                }
            }
        }
        if (count == nums.numsLen)
        {
            flag = true;
        }
    }
    if (nums.numsPtr == 0)
    {
        flag = true;
    }
    return flag;
}
/*--------------------------------------------------------*/
//14--返回数据出现的次数
template <typename tp>
size_t numbers<tp>::operator/(tp num) const
{
    size_t count = 0;
    if (numsPtr != nullptr)
    {
        for (size_t i = 0; i < numsLen; i++)
        {
            if (this->at(i) == num)
            {
                count++;
            }
        }
    }
    return count;
}
//15--返回两个对象共同的元素
template <typename tp>
numbers<tp> numbers<tp>::operator/(const numbers<tp> &nums) const
{
    numbers<tp> r_nums;
    if ((numsPtr != nullptr)&&(nums.numsPtr != nullptr))
    {
        for (size_t i = 0; i < nums.numsLen; i++)
        {
            if ((*this)% (*(nums.numsPtr + i)))
            {
                r_nums += (*(nums.numsPtr + i));
            }
        }
    }
    return r_nums;
}
/*--------------------------------------------------------*/
//16--删除元素
template <typename tp>
numbers<tp> numbers<tp>::operator-(const tp num) const
{
    numbers<tp> r_nums;
    if (numsPtr != nullptr)
    {
        for (size_t i = 0; i < numsLen; i++)
        {
            if (this->at(i) != num)
            {
                r_nums += *(numsPtr + i);
            }
        }
    }
    return r_nums;
}
//17--删除父对象中与子对象中重复的元素
template <typename tp>
numbers<tp> numbers<tp>::operator-(const numbers<tp>& nums) const
{
    numbers<tp> r_nums;
    size_t count = 0;
    if ((numsPtr != nullptr)&&(nums.numsPtr != nullptr))
    /*两个非空数字串*/
    {
        r_nums = *this;
        for (size_t i = 0; i < nums.numsLen; i++)
        {
            if ((*this)%(*(nums.numsPtr + i)))
            {
                r_nums = r_nums - *(nums.numsPtr + i);    
            }     
        }
        if (r_nums.numsLen == 0)
        {
            delete [] r_nums.numsPtr;
            r_nums.numsPtr = nullptr;
        }
    }
    else if ((numsPtr != nullptr)&&(nums.numsPtr == nullptr))
    /*子串为空*/
    {
        r_nums = *this;
    }
    return r_nums;
}
//18--按索引删除元素(数据必须是size_t类型)失败返回空串
template <typename tp>
numbers<tp> numbers<tp>::operator-(const size_t index) const
{
    numbers<tp> r_nums;
    if ((index < numsLen ) && (numsPtr != nullptr))
    {
        for (size_t i = 0; i < index; i++)
        {
            r_nums = r_nums + *(numsPtr + i);
        }
        for (size_t i = index + 1; i < numsLen; i++)
        {
            r_nums = r_nums + *(numsPtr + i);
        }
    }
    return r_nums;
}

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

使用道具 举报

发表于 2019-11-22 01:15:44 | 显示全部楼层
嗯。。首先,模板的声明和实现不能分开写到两个文件里。。。
除非你在cpp文件里将其显式的实例化
template class numbers<int>;
template class numbers<char>;
...
不然无法正确实例化;
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-11-22 02:10:42 From FishC Mobile | 显示全部楼层
本帖最后由 bin554385863 于 2019-11-22 02:16 编辑
Croper 发表于 2019-11-22 01:15
嗯。。首先,模板的声明和实现不能分开写到两个文件里。。。
除非你在cpp文件里将其显式的实例化



我这个压根就没考虑char类型,而且在vscode调试也没出现啥问题啊,别的编译器就不知道了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-11-22 17:43:00 | 显示全部楼层
是么?
你vscode是怎么配置的,用的什么编译器?

据我了解,.cpp文件在编译时应该是先生成.obj文件再进行连接的,如果生成文件时模板没有实例化,那么连接之后是会找不到相应代码的。
我想你是不是main.cpp里include了numbers.cpp?

测试代码:main.cpp:
#include <iostream>
#include "numbers.h"

using namespace std;

int main() {
        numbers<int> a = { 1,2,3,4,5,6 };
        auto b = a + 3;
        cout << b << endl;
        system("pause");
}

vscode 使用MinGW64编译:
> Executing task: g++ '-g' 'e:\vscode source file\c++\Test Projection\main.cpp' '-std=c++14' '-o' 'main.exe' <

C:\Users\ADMINI~1\AppData\Local\Temp\ccTiogBg.o: In function `main':
e:/vscode source file/c++/Test Projection/main.cpp:7: undefined reference to `numbers<int>::numbers(std::initializer_list<int>)'
e:/vscode source file/c++/Test Projection/main.cpp:8: undefined reference to `numbers<int>::operator+(int) const'
C:\Users\ADMINI~1\AppData\Local\Temp\ccTiogBg.o: In function `operator<<(std::ostream&, numbers<int> const&)':
e:/vscode source file/c++/Test Projection/numbers.h:34: undefined reference to `numbers<int>::size() const'
e:/vscode source file/c++/Test Projection/numbers.h:37: undefined reference to `numbers<int>::operator[](unsigned long long) const'
collect2.exe: error: ld returned 1 exit status
终端进程已终止,退出代码: 1

使用vs2017编译,也是同样的问题:
1>------ 已启动生成: 项目: Project1, 配置: Debug x64 ------
1>源1.cpp
1>源1.obj : error LNK2019: 无法解析的外部符号 "public: __cdecl numbers<int>::numbers<int>(class std::initializer_list<int>)" (??0?$numbers@H@@QEAA@V?$initializer_list@H@std@@@Z),该符号在函数 main 中被引用
1>源1.obj : error LNK2019: 无法解析的外部符号 "public: unsigned __int64 const __cdecl numbers<int>::size(void)const " (?size@?$numbers@H@@QEBA?B_KXZ),该符号在函数 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class numbers<int> const &)" (??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBV?$numbers@H@@@Z) 中被引用
1>源1.obj : error LNK2019: 无法解析的外部符号 "public: int & __cdecl numbers<int>::operator[](unsigned __int64)const " (??A?$numbers@H@@QEBAAEAH_K@Z),该符号在函数 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class numbers<int> const &)" (??6@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEAV01@AEBV?$numbers@H@@@Z) 中被引用
1>源1.obj : error LNK2019: 无法解析的外部符号 "public: class numbers<int> __cdecl numbers<int>::operator+(int)const " (??H?$numbers@H@@QEBA?AV0@H@Z),该符号在函数 main 中被引用
1>C:\Users\Administrator\source\repos\Project1\x64\Debug\Project1.exe : fatal error LNK1120: 4 个无法解析的外部命令
1>已完成生成项目“Project1.vcxproj”的操作 - 失败。

把numbers.cpp内的内容全部扔到numbers.h下后,两程序均输出
1 2 3 4 5 6 3
符合预期






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

使用道具 举报

 楼主| 发表于 2019-11-23 00:34:40 | 显示全部楼层
Croper 发表于 2019-11-22 17:43
是么?
你vscode是怎么配置的,用的什么编译器?

我包含的cpp,不是.h
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-11-23 14:18:26 | 显示全部楼层
本帖最后由 Croper 于 2019-11-23 14:19 编辑

任何时候都不应该包含.cpp文件,这完全失去了把.h文件和.cpp文件写成两部分的意义。(虽然这段代码本来.h文件和.cpp文件就写不开)
当你包含.cpp文件时,实际是把这两个文件写一块了。

如果你包含.cpp文件。在编译时,程序上的任何改动都会导致程序将整段程序重新编译,而不是仅仅重新编译单个模块。并且,被包含部分的代码会被反复多次编译。大大降低编译效率。
其次,一旦.cpp文件里包含static修饰的全局变量,那么static关键字将失去效果。导致一些不可预期的错误。毕竟模块化的思想是你不需要了解模块的实现,而只需要了解接口和功能。

csdn上有很多相关的文章,你可以去查查。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-11-23 17:18:22 | 显示全部楼层
本帖最后由 bin554385863 于 2019-11-23 17:21 编辑
Croper 发表于 2019-11-23 14:18
任何时候都不应该包含.cpp文件,这完全失去了把.h文件和.cpp文件写成两部分的意义。(虽然这段代码本来.h文 ...


如果我把类模板的定义和成员函数的实现分开写然后在写一个头文件同时包含进去,应该没问题吧
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-11-23 23:49:57 | 显示全部楼层
我感觉没啥问题,不过两部分应该都写成.h。

不过这样是不是有点为了分开而分开了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-10-4 23:24

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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