如何分配字符串文本

How to assign a string literal

本文关键字:字符串 文本 分配 何分配      更新时间:2023-10-16

我是一名Java程序员;我上一次写C或C++是在20年前。现在我回来了,我正在尝试使用更现代的C++,例如 C++11/C++14,并避免旧的 c 风格编程。

如何分配字符串,就像在 Java 中一样:

private static final String MY_STRING = "Hello World"; // could also be public

现在我有了对文本"Hello World"MY_STRING的引用,我可以在我的程序中根据需要多次使用,如果我想将其更改为"Hello Dolly",我完全在一个地方进行更改。

我想在C++实现同样的目标。 例:

JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = (char *)"-Djava.class.path=.";

这有效,没有更多的问题,但我不喜欢它,因为要求只有在需要时才能够在一个地方更改它。 所以,我声明(即使在头文件中):

std::string classpath = "-Djava.class.path=.";
// or
const std::string classpath = "-Djava.class.path=.";

我将其用作:

options[0].optionString = (char *)classpath.c_str();

现在,使用c_str()函数,这也可以工作,但我又回到了普通的旧 C!

我真的很想知道是否有办法保持在更现代的C++水平?

是的,我明白

JavaVMOption* options = new JavaVMOption[1]; 

按原样声明,我无法更改它。但即使在C++,在给定的情况下使用引用而不是指针也是一个好主意,就像在 Java 中一样。有解决办法吗?我找不到。

"Hello"分别是值 6char的常量缓冲区Hello其生命周期是整个程序。

std::string bob = "Hello";

将该缓冲区复制到std::string对象中。std::string是一个值语义对象;这是Java往往缺乏的东西,因为所有对象都通过引用隐式传递到处。

options[0].optionString = (char *)"-Djava.class.path=.";

这是极其危险的。 将该缓冲区强制转换为指向char的非const指针,然后将其分配给optionString

我不知道optionString是什么,但如果它是char*类型的变量,这将为您打开未定义的行为。 对缓冲区"-Djava.class.path=."的任何编辑都是未定义的行为,存储指向此类缓冲区的非const指针只是乞求发生这种情况。

简而言之,optionString的类型是决定其危险程度的关键。 仅仅是鲁莽,还是真的愚蠢?

JavaVMOption* options = new JavaVMOption[1];

这会在堆上创建一个大小JavaVMOption数组1然后在options中存储指向第一个元素的指针。

这里有很多毫无意义的事情。 摧毁它需要将其作为一个数组销毁吗?

JavaVMOption options;

这会在堆栈上创建一个名为optionsJavaVMOption。 它在范围结束时自动销毁。 这似乎更实用。 除非你需要new,否则使用new是没有意义的,而且你很少需要newC++。

我找到了一些示例代码:

#include <jni.h>       /* where everything is defined */
...
JavaVM *jvm;       /* denotes a Java VM */
JNIEnv *env;       /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = "-Djava.class.path=/usr/lib/java";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
/* load and initialize a Java VM, return a JNI interface
* pointer in env */
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
delete options;
/* invoke the Main.test method using the JNI */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "test", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);
/* We are done. */
jvm->DestroyJavaVM();

这不是由以C++编程为生的人写的。

这是我最初的尝试:

struct destroy_java_vm {
void operator()(JavaVM* jvm)const{
jvm->DestroyJavaVM();
}
};
using up_java_vm = std::unique_ptr< JavaVM, destroy_java_vm >;
struct java_vm {
up_java_vm vm;
JNIEnv* env = 0;
};
struct java_vm_option {
std::string string;
std::shared_ptr<void> extra_info;
};
using java_vm_options = std::vector<java_vm_option>;
struct java_vm_init {
unsigned version = JNI_VERSION_1_2;
java_vm_options options;
bool ignore_unrecognized = false;
java_vm init() {
std::vector<JavaVMOption> java_options(options.size());
for (std::size_t i = 0; i < options.size(); ++i) {
java_options[i].optionString = &options.string[0];
java_options[i].extraInfo = options.extra_info.get();
}
JavaVMInitArgs args;
args.version = version;
args.options = java_options.data();
args.nOptions = java_options.size();
args.ignoreUnrecognized = ignore_unrecognized?TRUE:FALSE;
java_vm retval;
JavaVM* tmp = 0;
auto res = JNI_CreateJavaVM(&tmp, (void **)&retval.env, &args);
if (res < 0) {
return {}; // error message?  How?
}
retval.vm.reset( tmp );
return retval;
}
}

用:

java_vm_init init;
init.options.push_back("-Djava.class.path=.");
auto vm = init.init();
if (!vm.env) { return /* error case */; }
/* invoke the Main.test method using the JNI */
jclass cls = vm.env->FindClass("Main");
jmethodID mid = vm.env->GetStaticMethodID(cls, "test", "(I)V");
vm.env->CallStaticVoidMethod(cls, mid, 100);
/* We are done. */

根据我对unique_ptr的使用,java_vm对象是一种仅移动的值类型。 销毁后,Java VM 将自动销毁(如果未将其移出)。

在直接与 API 对话时,我们必须转向 C 风格的代码,因为许多跨语言 API 都是使用 C 风格的接口编写的。 我们将 C 风格的界面包装在更安全、更易于使用的C++类型中。

代码未经测试,可能包含拼写错误。

我是一名Java程序员,在尝试使用JNI从C++调用Java时,我发现了同样的例子。 也就是说,我不知道这是否"正确",但它通过更改原始代码使代码可以编译而没有错误:

JavaVMOption* options = new JavaVMOption[1];   // JVM invocation options
options[0].optionString = "-Djava.class.path=."

改用字符数组。

JavaVMOption* options = new JavaVMOption[1];
char classpath[] = "-Djava.class.path=.";
options[0].optionString = classpath;

我的基本理解是 char* 与 char[] 相同。