std::cout &operator<< 的奇怪行为

发布于 2024-08-29 21:03:34 字数 2138 浏览 2 评论 0 原文

我今天遇到了一些奇怪的事情,我想知道这里的任何人是否可以解释发生了什么......

这是一个示例:

#include <iostream>
#include <cassert>
using namespace std;

#define REQUIRE_STRING(s)           assert(s != 0)
#define REQUIRE_STRING_LEN(s, n)    assert(s != 0 || n == 0)

class String {
public:
        String(const char *str, size_t len) : __data(__construct(str, len)), __len(len) {}
        ~String() { __destroy(__data); }

        const char *toString() const {
            return const_cast<const char *>(__data);
        }

        String &toUpper() {
            REQUIRE_STRING_LEN(__data, __len);
            char *it = __data;
            while(it < __data + __len) {
                if(*it >= 'a' && *it <= 'z')
                    *it -= 32;
                ++it;
            }
            return *this;
        }

        String &toLower() {
            REQUIRE_STRING_LEN(__data, __len);
            char *it = __data;
            while(it < __data + __len) {
                if(*it >= 'A' && *it <= 'Z')
                    *it += 32;
                ++it;
            }
            return *this;
        }

private:
        char *__data;
        size_t __len;

protected:
        static char *__construct(const char *str, size_t len) {
            REQUIRE_STRING_LEN(str, len);
            char *data = new char[len];
            std::copy(str, str + len, data);
            return data;
        }

        static void __destroy(char *data) {
            REQUIRE_STRING(data);
            delete[] data;
        }
};

int main() {
    String s("Hello world!", __builtin_strlen("Hello world!"));

    cout << s.toLower().toString() << endl;
    cout << s.toUpper().toString() << endl;

    cout << s.toLower().toString() << endl << s.toUpper().toString() << endl;

    return 0;
}

现在,我预计输出是:

hello world!
HELLO WORLD!
hello world!
HELLO WORLD!

但我得到的是:

hello world!
HELLO WORLD!
hello world!
hello world!

我真的无法理解为什么第二个 toUpper 没有任何效果。

I came across something weird today, and I was wondering if any of you here could explain what's happening...

Here's a sample:

#include <iostream>
#include <cassert>
using namespace std;

#define REQUIRE_STRING(s)           assert(s != 0)
#define REQUIRE_STRING_LEN(s, n)    assert(s != 0 || n == 0)

class String {
public:
        String(const char *str, size_t len) : __data(__construct(str, len)), __len(len) {}
        ~String() { __destroy(__data); }

        const char *toString() const {
            return const_cast<const char *>(__data);
        }

        String &toUpper() {
            REQUIRE_STRING_LEN(__data, __len);
            char *it = __data;
            while(it < __data + __len) {
                if(*it >= 'a' && *it <= 'z')
                    *it -= 32;
                ++it;
            }
            return *this;
        }

        String &toLower() {
            REQUIRE_STRING_LEN(__data, __len);
            char *it = __data;
            while(it < __data + __len) {
                if(*it >= 'A' && *it <= 'Z')
                    *it += 32;
                ++it;
            }
            return *this;
        }

private:
        char *__data;
        size_t __len;

protected:
        static char *__construct(const char *str, size_t len) {
            REQUIRE_STRING_LEN(str, len);
            char *data = new char[len];
            std::copy(str, str + len, data);
            return data;
        }

        static void __destroy(char *data) {
            REQUIRE_STRING(data);
            delete[] data;
        }
};

int main() {
    String s("Hello world!", __builtin_strlen("Hello world!"));

    cout << s.toLower().toString() << endl;
    cout << s.toUpper().toString() << endl;

    cout << s.toLower().toString() << endl << s.toUpper().toString() << endl;

    return 0;
}

Now, I had expected the output to be:

hello world!
HELLO WORLD!
hello world!
HELLO WORLD!

but instead I got this:

hello world!
HELLO WORLD!
hello world!
hello world!

I can't really understand why the second toUpper didn't have any effect.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

巷子口的你 2024-09-05 21:03:34

这都是因为您的代码

cout << s.toLower().toString() << endl << s.toUpper().toString() << endl;

以及 toLowertoUpper 的实现方式。以下代码应该按预期工作

cout << s.toLower().toString() << endl;
cout << s.toUpper().toString() << endl;

问题是 toLowertoUpper 不会创建新对象,而是修改现有对象。当您在同一个块中调用多个修改方法并将该对象作为参数传递到某处时,行为是未定义的。

编辑:这类似于流行的问题,这会是什么结果

int i = 5;
i += ++i + i++;

这里的正确答案是相同的:未定义。您可以在 C++ 中搜索“序列点”以获得更深入的解释。

This is all because of your code

cout << s.toLower().toString() << endl << s.toUpper().toString() << endl;

and how toLower and toUpper are implemented. Following code should work as expected

cout << s.toLower().toString() << endl;
cout << s.toUpper().toString() << endl;

The issue is that toLower and toUpper don't create a new object but modify existing object. And when you call several modifying methods in the same block AND pass this object somewhere as argument, behavior is undefined.

EDIT: This is similar to popular question, what would be result of

int i = 5;
i += ++i + i++;

The correct answer here is the same: undefined. You can google for "sequence points" in C++ for deeper explanation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文