我想做的是欺骗类成员变量的常量性。所以我有以下代码:
“StringView.hh”:
#pragma once
#include <cstring>
class StringView {
public:
const char* _begin = nullptr;
const char* _end = nullptr;
explicit StringView(const char* str) : _begin(str), _end(str + strlen(str)) {}
};
main.cpp:
#include <iostream>
#include "StringView.hh"
int main() {
char str[] = "cat";
StringView sv(str);
std::cout << str << '\n';
void *pointer = reinterpret_cast<void *>(&sv);
// *(char *) pointer = 'b'; // I would also like to set it to a whole string, but that doesn't work, it would work if it was std::string, not char *, maybe char ** would work?
// std::cout << str << '\n';
std::cout << "Pointer to StringView: " << pointer << "\n";
std::cout << "String: " << (void *)str << "\n";
std::cout << "String from StringView _begin: " << (void *)sv._begin << "\n";
return 0;
}
结果:
cat
Pointer to StringView: 0xe25bffde0
String: 0xe25bffdf4
String from StringView _begin: 0xe25bffdf4
因此该字符串和 StringView 中的字符串具有完全相同的地址。这种情况在 cmake 和 gcc 上都会发生,但 main.cpp 中的指针却不会。这是为什么?
当我取消注释代码行时,我得到:
cat
cat
Pointer to StringView: 0x81491ffc50
String: 0x81491ffc64
String from StringView _begin: 0x81491ffc62
现在这 3 个地址都有不同的地址。这是为什么?有什么办法可以欺骗这个,这样我就可以通过 const 限定符而不使用明显的
const_cast<>()
?这在 c 中也不起作用(使用结构、删除显式、使用 printf 等)。
我知道不在对象中的 const 字符串(例如 StringView sv)存储在与变量内存不同的只读内存中。就像在全局变量/静态变量空间中一样,这就是为什么我将 str 设置为 const char[] 的原因,并且它仍然位于其他一些内存中,该内存会根据我是否更改该内存和其他内容而发生变化。 另外,我没有使用 std::string,因为它有一个重载的运算符 =,这会破坏 const-ness,因为在这种情况下它是不同的。
默认情况下 _begin 和 _end 应该是私有的,我只是将它们公开以检查地址。
c 中的代码:
#include <stdio.h>
#include <string.h>
struct StringView {
const char* _begin;
const char* _end;
};
struct StringView create(const char *str) {
struct StringView a = {str, str + strlen(str)};
return a;
}
int main() {
char str[] = "cat";
struct StringView sv = create(str);
printf("%s\n", str);
void *pointer = (void *) &sv;
*(char *) pointer = 'b';
printf("%s\n", str);
printf("Pointer to StringView: %p\n", pointer);
printf("String: %p\n", (void *)str);
printf("String from StringView _begin: %p\n", (void *)sv._begin);
return 0;
}
在这些声明中
char str[] = "猫"; StringView sv(str);
指针 str 指向的字符串文字占用其自己的内存范围,而对象 sv 也拥有自己的内存范围。因此这些陈述
std::cout << "Pointer to StringView: " << pointer << "\n";
和
std::cout << "String: " << (void *)str << "\n";
std::cout << "String from StringView _begin: " << (void *)sv._begin << "\n";
输出不同内存范围的地址。
本声明
*(char *) pointer = 'b';
仅调用未定义的行为。