C++ - 为我的字符串类重载运算符>>

Posted

技术标签:

【中文标题】C++ - 为我的字符串类重载运算符>>【英文标题】:C++ - overloading operator >> for my string class 【发布时间】:2015-10-22 08:30:57 【问题描述】:

我实现了字符串类MyString。这是代码:

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class MyString
    private:
    char * content;
    int length;
    void copy(const MyString & source);
    public:
    MyString();
    MyString(const char * source);
    ~MyString();
    MyString(const MyString & source);
    void print(void);
    MyString & operator = (const MyString &source);
    friend std::ostream & operator << (std::ostream & out, const MyString&   towrite);
    friend std::istream & operator >> (std::istream & in, MyString & toread);
;

MyString::MyString()
    content = new char[1];
    content[0] = '\0';
    length = 0;



MyString::MyString(const char *source)
    length = strlen(source);
    content = new char[length + 1];
    strcpy(content, source);


MyString::~MyString()
    delete[] content;


void MyString::copy(const MyString & source)
    length = source.length;
    content = new char[length + 1];
    strcpy(content, source.content);


MyString::MyString(const MyString & source)
    copy(source);


void MyString::print(void)
    cout << "" << content << endl;


MyString &MyString::operator=(const MyString &source)
    copy(source);
    return *this;


std::ostream & operator<<(std::ostream & out,const MyString& towrite)
    out << towrite.content;
    return out;


std::istream & operator >> (std::istream & in, MyString & toread)
    int length;
    std::cout << "Enter length of word: " << endl;
    std::cin >> length;
    toread.length = length;
    toread.content = new char[toread.length+1];
    for (int i = 0; i < toread.length; i++)
    in >> toread.content[i] ;
    
    toread.content[toread.length] = '\0';
    return in;
 

我的问题与重载运算符有关>>。

对于这个主程序:

int main()
    MyString word;
    std::cout<<"Enter some word: "<<endl;
    std::cin>>word;
    std::cout<<"Your entered: "<<word<<endl;

这是输出:

Enter some word:

Enter length of word:

5

stack

Your entered: stack

Process returned 0 (0x0)   execution time : 8.313 s

Press any key to continue.

它正确打印用户输入的字符串,但它不会按照我想要的方式“模仿”原始字符串类。这就是原因。

如果使用 C++ 字符串类:

int main()
    std::string word;
    std::cout<<"Enter some word: "<<endl;
    std::cin>>word;
    std::cout<<"Your entered: "<<word<<endl;

用户无需输入字长。我可以通过我的班级实现这一目标吗?

编辑1:

我是这样做的:

std::istream & operator >> (std::istream & in, MyString & toread)
    char *temp;
    temp = new char[100];
    char c;
    int i = 0;
    while(c != '\n')
    c = getchar();
    temp[i++] = c;
    
    temp[i] = '\0';
    int length = i-1;
    toread.length = length;
    toread.content = new char[toread.length+1];
    for(int i = 0 ; i < toread.length ; i++)
    toread.content[i] = temp[i];
    
    delete [] temp;
    toread.content[toread.length+1]='\0';

它可以正常工作。但是,我收到警告,因为我没有返回“in”:

||=== 构建:在 fdsfsdf 中调试(编译器:GNU GCC 编译器)===| C:\Users\hae\Desktop\fdsfsdf\main.cpp||在函数'std::istream& operator>>(std::istream&, MyString&)':| C:\Users\hae\Desktop\fdsfsdf\main.cpp|137|警告:函数中没有返回语句返回非 void [-Wreturn-type]| ||=== 构建完成:0 个错误,1 个警告(0 分钟,4 秒)===| ||=== 运行:在 fdsfsdf 中调试(编译器:GNU GCC Compiler)===|

【问题讨论】:

是的。调用istream::operator&gt;&gt;(std::string),然后从std::string 构造您的字符串(使用c_str())。 operator&gt;&gt;(std::istream&amp;, std::string&amp;) 读取直到找到第一个空白字符。你也可以那样做。此外,您正在泄漏内存。 你也没有释放你的记忆。首先,您的默认字符串构造函数分配内存并具有指向它的指针,然后当您获得输入时,您忘记释放该内存。我还会研究一下 std::allocator ,这将是一种更有效的分配和构造内存的方法。 在你的默认构造函数中你总是。赋值永远不会解除分配。 @elf 例如,在您的复制方法中,您应该首先删除该指针指向的对象的 char * 内存,然后使 char * 内容指向新分配的内存。其他地方也一样。 【参考方案1】:

这是我很久以前写的类似课程的精简版。这是一个古董,但它应该可以工作,并解决你的班级的一些问题。

class charray 
public:
    charray();
    ~charray();

    charray(const charray&);
    charray(const char*);

    charray& operator=(const charray&);
    charray& operator=(const char*);

    void swap(charray&);

    const char* c_str() const
     return m_elem; 

    unsigned int size() const
     return m_size; 

private:
    void m_resize(unsigned int size);

    char* m_elem;
    unsigned int m_size;
;

// private.
void charray::m_resize(unsigned int size)

    char* elem = new char[size+1];

    memcpy(elem, m_elem, std::min(m_size, size));
    elem[size] = '\0';
    delete [] m_elem;

    m_elem = elem;
    m_size = size;


// public.
charray::charray()
    : m_elem(0), m_size(0)

    m_resize(0);


charray::~charray()

    delete [] m_elem;


charray::charray(const charray& src)
    : m_elem(0), m_size(0)

    unsigned int size = src.size();
    m_resize(size);
    memcpy(m_elem, src.c_str(), size);


charray::charray(const char* src)
    : m_elem(0), m_size(0)

    unsigned int size = std::strlen(src);
    m_resize(size);
    memcpy(m_elem, src, size);


charray& charray::operator=(const charray& rhs)

    charray temp(rhs);
    this->swap(temp);
    return *this;


charray& charray::operator=(const char* rhs)

    charray temp(rhs);
    this->swap(temp);
    return *this;


void charray::swap(charray& b)
 
    std::swap(m_elem, b.m_elem);
    std::swap(m_size, b.m_size);

这可能是您最感兴趣的内容。请密切注意细节。在直接处理内存时,工作实现和损坏的实现之间的区别通常非常微妙。

注意:运营商不是朋友。他们不访问私人数据。

std::ostream& operator<<(std::ostream& out, const charray& in)

    return out << in.c_str();


std::istream& operator>>(std::istream& in, charray& out)

    // verify no errors are set, flush tied streams, strip leading
    // whitespace.
    std::istream::sentry sentry(in);
    if (!sentry)
        return in;

    unsigned int size = 0;
    unsigned int tail = 0;
    char* temp = 0;
    int next; // @note int not char (to record EOF).

    while ((next = in.get()) != in.eof() && !std::isspace(next)) 
        // if temp buffer is exhausted, then double the buffer size.
        // (base size of 16).
        if (tail == size) 
            unsigned int newsize = std::max(2*size, 16u);
            char* newtemp = new char[newsize+1];
            memcpy(newtemp, temp, size);
            delete [] temp;
            temp = newtemp;
            size = newsize;
        
        temp[tail++] = next;
    
    // @note because the stream is prepeared with istream::sentry, there
    // will be at least one non-whitespace character in the stream.
    assert(temp != 0);
    temp[tail] = '\0';
    out = temp;
    delete [] temp;
    return in;

做同样事情的更简单、更安全的方法,

#include <vector>
std::istream& operator>>(std::istream& in, charray& out)

    std::istream::sentry sentry(in);
    if (!sentry)
        return in;

    std::vector<char> temp;
    int next;

    while ((next = in.get()) != in.eof() && !std::isspace(next))
        temp.push_back(next);
    temp.push_back('\0');
    out = &temp[0];
    return in;

编辑 以上内容已过时(C++ 11 之前)。现代实现可能会以不同的方式处理构造和分配。以下是这些方法的更新版本,

注意: 方法m_resize 不见了。一切都通过构造函数处理。

charray::charray(const char* src, unsigned int size)
    : m_elem new char[size+1] , m_size size 

    std::copy(src, src + size, m_elem);


charray::charray()
    : charray(nullptr, 0)


charray::charray(const charray& src)
    : charray(src.m_elem, src.m_size)


charray::charray(const char* src)
    : charray(src, std::strlen(src))


charray::charray(charray&& src)
    : m_elem src.m_elem , m_size src.m_size 

    src.m_elem = nullptr;
    src.m_size = 0;


// handle both move and copy assignment.
charray& charray::operator=(charray rhs)

    this->swap(rhs);
    return *this;

希望这会有所帮助。祝你好运。

【讨论】:

以上是关于C++ - 为我的字符串类重载运算符>>的主要内容,如果未能解决你的问题,请参考以下文章

C++ 重载运算符 '>>'

C++:使用类|| 运算符重载友元

模板化一个类,然后重载运算符 (C++)

[C++]——日期类运算符的重载(针对Date类重载<,>,<=,>=,==,++,<<,>>运算符,并构建排序函数,将时间进行升序排序)

[C++]——日期类运算符的重载(针对Date类重载<,>,<=,>=,==,++,<<,>>运算符,并构建排序函数,将时间进行升序排序)

无法在 C++ 中重载流提取运算符 (>>)