在类构造函数中安全地连接 C 字符串

问题描述 投票:0回答:8

我有一个类需要在构造期间连接两个

const char*
字符串,甚至稍后在初始化列表中使用结果(连接的字符串)。

const char* SUFFIX = "suffix";

class widget {
public:
    widget(const char* prefix) : key(???), cls(key) { };

private:
    const char* key;
    const important_class cls;
}

widget("prefix_"); // key should be prefix_suffix

有一个全局(在

widget
的cpp中)
const char*
后缀,我想附加到用户提供的前缀。

怎么做?


顺便说一句。我听说过

string
。如果我可以使用
string
我不会在这里问,关于
const char*

c++ cstring
8个回答
3
投票

使用

std::string
使您的问题成为一项微不足道的任务:

const std::string SUFFIX = "suffix";

class widget {
public:
    widget(std::string const & prefix) 
           : key(prefix + SUFFIX), cls(key)
    { }       // ^^^^^^^^^^^^^^concatenation!

private:
    const std::string key;
    const important_class cls; 
}

widget("prefix_");

如果你需要

const char*
,你仍然可以通过调用
key.c_str()
得到它,它返回
const char*
。所以在你的情况下,它会给你 c-string
"prefix_suffix"
.

另请注意,声明的顺序很重要,您已正确完成:

cls
必须在之后
key
声明,因为其构造取决于
key
.


2
投票

std::string
作为中间值:

const char* SUFFIX = "suffix";

class widget {
public:
    widget(const char* prefix) :
    intermediate(string(prefix) + suffix),
    key(intermediate.c_str()),
    cls(key) {
    }

private:
    const std::string intermediate;
    const char* key;
    const important_class cls;
}

widget("prefix_"); // key should be prefix_suffix

此处与您的代码唯一不同的是私有成员变量,

intermediate
.

std::string
对象
intermediate
管理保持连接所需的动态内存。它会在异常、分配、复制等情况下很好地清理

只要

string
没有发生变化,
const char*
返回的
c_str()
将保持有效。由于
intermediate
const
,不能对其调用任何变异函数,因此使用内部缓冲区的
cls
不应该有问题。


1
投票

您应该使用 std::string (或您的项目使用的任何字符串类)。这将使这变得容易得多,并避免使这个异常安全的问题。

如果你坚持,

widget(const char* prefix) : 
    key(strcat(strcpy(new char[strlen(prefix)+strlen(SUFFIX)+1], prefix), SUFFIX)), 
    cls(key) { }

会用 C 的方式来做。

important_class
的构造函数不应该抛出异常。

如果您需要使用生成的

key
来初始化
cls
,请注意保持声明的顺序。


0
投票

最好使用

std::string
把你的弦放在一起。如果需要,您仍然可以致电
std::string::c_str()
获取
const char*
std::string::append
也以
const char*
作为参数。或者使用
+
-运算符。

这里.


0
投票

我不确定你能在初始化列表中做到这一点。当然,其他一些有更多经验的用户可以提供帮助......当然,这听起来很明显,但你可以使用 std::string 然后就可以了:

class widget 
{
    public:
    widget(const char* prefix) : key(prefix)
    { 
       string_key.append(suffix);
    };

    private:
    const char* key;
    std::string string_key;
};

当然,你可以这样做:

class widget 
{
    public:
    widget(const char* prefix) : key(prefix)
    { 
       key.append(suffix);
    };

    private:
    std::string key;
};

我错过了整个“important_class”部分。它在这里代表什么?你想达到什么目的?.

希望有帮助。


0
投票

我看到两个选项,具体取决于您对价值持久性等的需求

一个,将

key
存储为
std::string

const char* SUFFIX = "suffix";

class widget{
public:
  widget(const char* prefix) : key(std::string(prefix) + suffix), cls(key.c_str())
  {}

private:
  std::string key;
  const important_class cls;
};

二,使用静态初始化函数,像这样:

const char* SUFFIX = "suffix";

class widget{
public:
  widget(const char* prefix) : key(concat_(prefix, suffix)), cls(key)
  {}

private:
  const char* key;
  const important_class cls;

  static const char* concat_(const char* a, const char* b)
  {
    std::string val(a);
    val += b;
    char* ret = new char[val.size() + 1];
    strcpy(ret, val.c_str());
    return val;
  }
};

0
投票

乍一看你的问题像是理论测验,但后来我想起来可能有一个没有STL的嵌入式平台。
无论如何,你需要坚持使用老式的 C 风格函数:strlen、strcpystrcat.
由于您使用的是类,因此我假设您的编译器也支持 operator new 。 在我编写最终代码之前,您需要执行以下步骤来连接字符串:

const char* key;
key = new char[strlen(prefix) + strlen(SUFFIX) + 1];
strcpy(key, prefix);
strcat(key, SUFFIX);
...
delete [] key;

幸运的是,strcpy 和 strcat 都返回了目的地。
这是您的代码的外观:

#include <string.h>

class widget {
public:
  widget(const char* prefix)
    : key(strcat(strcpy(new char[strlen(prefix) + strlen(SUFFIX) + 1], prefix), SUFFIX))
    , cls(key) { };

private:
  const char* key;
  const important_class cls;
};

我没有调试这段代码,但它编译得很好,虽然它看起来很乱......:)
不要忘记在析构函数中释放缓冲区。
祝你好运!


0
投票

如果你不是

std::string
的粉丝,你仍然可以制作你的c风格
char*
构建器的自定义函数,并在构造函数中调用构建器。

在下面的示例中,父类
std::runtime_error(const char*)
已成功从其子类初始化,
你可以在孩子和他的父母之间发现任何类型的错误:

num::dividebyzero_error

std::runtime_error

std::exception

在任何情况下,err.what() 都会正确显示字符串。

// snprintf(char* result, size_t maxsize, const char* format, args...)
#include <iostream>
class num {
public:
    int value;
    num() : value(0) {}
    num(int newvalue) : value(newvalue) {}
    class dividebyzero_error : public std::runtime_error {
    private:
        const char* makemsg(int dividend) {
            char* msg = (char*)malloc(128);
            snprintf(msg, 128, "You should not divide %d into ZERO.", dividend);
            return msg;
        }
    public:
        dividebyzero_error(int dividend): std::runtime_error(makemsg(dividend)) {} // this works!
    };
    friend num operator/(const num &n1, const num &n2) {
        if (n2.value == 0) throw dividebyzero_error(n1.value);
        return num(n1.value / n2.value);
    }
};
int main() {
    num a(1234);
    num b;
    try {
        num c = a / b;
        std::cout << c.value << '\n';
    } catch (std::exception &err) {
        std::cout << err.what() << '\n';
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.