添加用户定义类型 C++

Posted

技术标签:

【中文标题】添加用户定义类型 C++【英文标题】:Addition on user-defined types C++ 【发布时间】:2014-08-10 09:46:46 【问题描述】:

我正在编写自己的类(称为“Longer”),这样它就可以容纳一个没有任何上限的数字,这与 int 不同。我为此使用 std::string 。 我在执行加法时遇到问题。

    如果我只是添加两个字符串,我无法得到正确的结果。 我想过将字符串转换为int,然后执行加法, 但长字符串无法转换为int。

如何定义我自己的添加两个字符串的方式,以便获得所需的结果?代码如下:

更长的.h

#pragma once
#include <string>

class Longer

public:
   Longer(std::string number);
   Longer add(Longer num2);
   void print();
private:
   std::string number;
;

更长的.cpp

#include "Longer.h"
#include <iostream>
#include <string>

Longer::Longer(std::string num): number(num)



Longer Longer::add(Longer num2)
  return Longer(number+num2.number);


void Longer::print()
std::cout<<number<<"\n";

main.cpp

#include <iostream>
#include "Longer.h"

int main()

Longer num1("123456789101112");
Longer num2("121110987654321");


Longer num3 = num1.add(num2);
num3.print();


【问题讨论】:

您最好使用数据类型long long intunsigned long long int。您不太可能需要比这更长的变量来保存一个数字。还有long double @Nav 这将如何解决一般问题?它是有界的,就像任何其他内置整数类型一样。 @juan:有一段时间我什至想过要创建这样一个程序。但是后来我了解到字符串操作要慢得多,并且关于long long int等的存在。如果程序员认为用户可能会键入一个异常长的数字,那么我觉得算法应该更专注于将数字拆分成更可行的可以比字符串更快地执行计算的组件(使用数字数据类型),而不是将数字用作字符串并尝试对其执行计算。 也许其他一些持有intmax_t的容器,比如std::vector&lt;intmax_t&gt;会更合适。对于这项工作,String 似乎完全是错误的容器。 @Nav 只要您已经在执行字符串操作,您也可以使用动态容器。没有人说这将用于高性能操作。 【参考方案1】:

我不奇怪加法不会像你想要的那样工作。 std::string 不打算用作任意长度的数字容器,这就是原因。

您必须定义自己的方式来“添加”两个字符串,这应该包括向后迭代两个字符串(从末尾开始)并比较单个字符,将它们解释为数字。

【讨论】:

+1 令人惊讶的是,加法对人们来说如此自然以至于他们“忘记”了它实际上是如何完成的 :) @FredOverflow +1 最有趣的学生项目之一是为 4 个基本整数运算做一个任意基数、无限位大小的计算器。经常启发你对算术的理解。【参考方案2】:

与 int 不同,没有任何上限

小心这些事情。任何解决方案总会有一些上限,至少当你的机器内存耗尽时。一个健壮的应用程序应该总是有某种错误检查。

如果我只是添加两个字符串,我无法得到正确的结果。

嗯,这很明显,不是吗?字符串连接对数学语义一无所知。

我想过将字符串转换为int然后执行加法, 但是长字符串不能转成int。

没错。在内部将字符串转换为内置类型会破坏解决方案的全部目的。

如何定义我自己的添加两个字符串的方式,以便获得 想要的结果?

目标显然是支持比内置类型提供的更大的数字。

首先,您真的确定您的应用程序需要处理如此庞大的数字吗?即使是标准的 int 通常也应该绰绰有余,更不用说 long long(自 C++11 以来的标准,但在此之前实际上可用)。

也许您真正需要的是检测无效的用户输入,例如“10000000000000000000000000”

字符串流为您提供这种错误检测。这是一个完整的示例供您使用,包括std::numeric_limits 的示例用法:

#include <iostream>
#include <stdexcept>
#include <exception>
#include <limits>

int ReadInt()

    int result;
    std::cin >> result;
    if (!std::cin)
    
        throw std::runtime_error("Illegal number");
    
    return result;


int main()

    try
    
        std::cout << "Enter number (max: " << std::numeric_limits<int>::max() << ") > ";
        int input = ReadInt();
        std::cout << "You entered the following number: " << input << "\n";
    
    catch (std::exception const &exc)
    
        std::cerr << exc.what() << "\n";
    

以下是在我的机器上运行的三个示例。第一个是“正常”的小数,第二个只是略大于可能的最大值,第三个恰好是可能的最大整数:

Enter number (max: 2147483647) > 1000
You entered the following number: 1000

Enter number (max: 2147483647) > 2147483648
Illegal number

Enter number (max: 2147483647) > 2147483647
You entered the following number: 2147483647

现在,如果您真的必须在内部支持大整数,请不要重新发明***。使用 Boost.Multiprecision

http://www.boost.org/doc/libs/1_55_0/libs/multiprecision/doc/html/index.html

由于该特定库的文档可能有点难以理解,这里有一个非常简单的示例来帮助您入门:

#include <iostream>
#include <stdexcept>
#include <exception>
#include <boost/multiprecision/cpp_int.hpp>

int main()

    try
    
        boost::multiprecision::int128_t number("100000000000000000000000000000000");

        number *= 2;

        std::cout << number << "\n";
    
    catch (std::exception const &exc)
    
        std::cerr << exc.what() << "\n";
    

这实际上打印了200000000000000000000000000000000

【讨论】:

【参考方案3】:
#include <iostream>
using namespace std;

class Longer 
public:
    Longer(std::string number): number(number) 
    void print()  cout << number << endl; 
    Longer add(Longer num2) 
        char over = '0'; string it;
        for(int i = number.size() - 1,
          j = num2.number.size() - 1;
          i >= 0 || j >= 0; i--, j--) 
            char one = i >= 0 ? number[i] : '0';
            char two = j >= 0 ? num2.number[j] : '0';
            char dig = one-'0' + two-'0' + over;
            over = '0'; if(dig > '9') 
                dig -= 10; over = '1'; 
            it.insert(0, 1, dig);
        
        if(over != '0') it.insert(0, 1, over);
        return Longer(it);
    
private:
   std::string number;
;

int main() 
    Longer num1("123456789101112"); num1.print();
    Longer num2("121110987654321"); num2.print();
    Longer num3 = num1.add(num2);   num3.print();

输出:

123456789101112 121110987654321 244567776755433

但如果那不是家庭作业,请查看boost::multiprecision::cpp_int

【讨论】:

【参考方案4】:

这是一个现成的解决方案

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <iterator>

class Longer

public:
    Longer() : value( 1, '0' ) 

    Longer (std::string s ) 
        : value( s.rbegin(), s.rend() )
    

    Longer( const char *s ) 
        : value( std::reverse_iterator<const char *>( s + std::strlen( s ) ),
                 std::reverse_iterator<const char *>( s ) )
                 

    const Longer add( const Longer &number ) const;

    void print( std::ostream &os = std::cout ) const
    
        os << std::string( value.rbegin(), value.rend() );
    

private:
    std::string value;
;

const Longer Longer::add( const Longer &number ) const

    std::pair<std::string::size_type, std::string::size_type> sizes = 
        std::minmax( this->value.size(), number.value.size() );

    std::string result;
    result.reserve( sizes.second + 1 );

    int overflow = 0;

    auto out = std::transform( this->value.begin(), 
                               std::next( this->value.begin(), sizes.first ),
                               number.value.begin(), 
                               std::back_inserter( result ),
                               [&] ( char c1, char c2 ) ->char
                               
                                   char c = ( c1 - '0' ) + ( c2 -'0' ) + overflow;
                                   overflow = c / 10;
                                   return c % 10 + '0';
                                );

    std::string::const_iterator first, last;

    if ( this->value.size() < number.value.size() )
    
        first = std::next( number.value.begin(), sizes.first );
        last  = number.value.end();
    
    else
    
        first = std::next( this->value.begin(), sizes.first );
        last  = this->value.end();
    

    std::transform(first, last, out,
                   [&]( char c )
                   
                       return ( c = c - '0' + overflow ), 
                              ( overflow = c / 10 ),
                              ( c % 10 + '0' );  
                    );

    if ( overflow ) result.push_back( overflow + '0' );

    Longer n;
    n.value = result;

    return n;




int main() 

    Longer n1( "12345678912345678" );

    n1.print();
    std::cout << std::endl;

    Longer n2( "1123" );

    n2.print();
    std::cout << std::endl;

    Longer n3 = n2.add( "877" );

    n3.print();
    std::cout << std::endl;

    Longer n4( "9999999999" );

    n4.print();
    std::cout << std::endl;

    Longer n5 = n4.add( "1" );

    n5.print();
    std::cout << std::endl;

    return 0;

输出是

12345678912345678
1123
2000
9999999999
10000000000

考虑到在类中以相反的顺序存储字符串更方便。

【讨论】:

以上是关于添加用户定义类型 C++的主要内容,如果未能解决你的问题,请参考以下文章

将任意文本添加到 fmt 的用户定义类型格式化程序

有没有办法添加用户使用 C++ 输入的行数?

Jmeter添加变量的四种方法

用户定义类型的 getter 和 setter 上的 C++(类似 CAS)同步

如何在 C++ 中向向量中添加新对象

FMT C++ 库:允许用户为自定义类型设置格式说明符