2013-07-04 2 views
15

NULLchar 포인터에서 초기화 std::string 포인터는 정의되지 않은 동작입니다.NULL char 포인터에서 std :: string을 초기화하십시오.

void MyClass::MyClass(const char *cstr) : 
    mStdString(cstr ? cstr : "") 
{} 

void MyClass::MyClass(const char *cstr) : 
    mStdString(cstr ? std::string(cstr) : std::string()) 
{} 

void MyClass::MyClass(const char *cstr) 
{ 
    if (cstr) mStdString = cstr; 
    // else keep default-constructed mStdString 
} 

편집, class MyClass 내부 생성자 선언 : 그래서, 여기에 mStdString 유형 std::string의 멤버 변수 인 생성자의 대체 버전입니다 다른

MyClass(const char *cstr = NULL); 
이들의

, 또는 아마도 뭔가, 가능하면 NULL 포인터에서 std::string을 초기화하는 가장 좋은 또는 가장 적절한 방법은 무엇입니까? C++ 표준마다 다른가요? 정상적인 릴리즈 빌드 최적화 플래그를 가정합니다.

이유가 올바른 방법 인 이유에 대한 설명이나 참조 링크가있는 답변에 대한 답변을 찾고 있습니다. (개인 의견뿐만 아니라 대답이 "중요하지 않은 경우에도 적용됩니다" 그러나 꼭해야한다면, 적어도 코멘트로 만드십시오).

+0

모두 nullptr을 원하는대로 결정합니다. 빈 문자열과 같습니까? 아니면 다른 의미를 가져야합니까? – PlasmaHH

+0

문자열의 생성자를 한 번만 호출하기 때문에 첫 번째 옵션을 사용해야한다고 생각합니다. – Alexis

+1

@PlasmaHH 글쎄, 그것은 코드 포인터가 NULL 일 때 코드 스 니펫이하는 일을하고 싶습니다. – hyde

답변

15

가능한 한 초기화를 사용하지 않기 때문에 마지막 것은 어리 석다.

첫 번째 두 개는 완전히 의미 론적으로 동일하므로 (첫 번째 버전은 c_str() 멤버 함수를 생각해보십시오.) 가장 직접적이고 관용적이며 읽기 쉽기 때문에 첫 번째 버전을 선호합니다. std::stringconstexpr 기본 생성자가 있다면

은 ( 의미 론적 차이가있을 것입니다,하지만. 아직도, 그것은 std::string()std::string("") 다른 것을 가능하지 않습니다,하지만 난 할 어떤 구현을 모르는 이, 많은 이해를 할 것 같지 않기 때문이다. 반면에, 인기 작은 문자열 최적화 요즘 두 버전은 아마 는 동적 할당을 수행하지 않습니다 것을 의미한다.)


업데이트 : @ 조나단 지적으로, 두 개의 문자열 생성자는 아마 다른 코드를 실행, 그리고 당신에게 중요한 경우 (정말 안하지만), 당신이 네 번째 버전을 고려해 볼 수 있습니다 :

: cstr ? cstr : std::string() 

모두를 읽기 쉽고 기본값을 구성합니다.


두 번째 업데이트 : 그러나이 cstr ? cstr : ""을 선호합니다. 아래에서 볼 수 있듯이 두 가지 브랜치가 같음 생성자를 호출하면 조건부 이동과 분기를 사용하여 매우 효율적으로 구현할 수 있습니다. (그래서 두 가지 버전이 실제로 서로 다른 코드를 생성 않지만, 첫 번째가 더 낫다.)

웃음 들어

, 나는 연타 3을 통해 두 버전을 실행했습니다.3, 당신과 기능 foo bar(char const * p) { return p; } 같은 struct foo;에 대한 x86_64에에 -O3,과 :

기본 생성자 (std::string() 일) :

.cfi_offset r14, -16 
    mov  R14, RSI 
    mov  RBX, RDI 
    test R14, R14 
    je  .LBB0_2 
    mov  RDI, R14 
    call strlen 
    mov  RDI, RBX 
    mov  RSI, R14 
    mov  RDX, RAX 
    call _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm 
    jmp  .LBB0_3 
.LBB0_2: 
    xorps XMM0, XMM0 
    movups XMMWORD PTR [RBX], XMM0 
    mov  QWORD PTR [RBX + 16], 0 
.LBB0_3: 
    mov  RAX, RBX 
    add  RSP, 8 
    pop  RBX 
    pop  R14 
    ret 

빈 문자열 생성자 ("" 일) :

.cfi_offset r14, -16 
    mov  R14, RDI 
    mov  EBX, .L.str 
    test RSI, RSI 
    cmovne RBX, RSI 
    mov  RDI, RBX 
    call strlen 
    mov  RDI, R14 
    mov  RSI, RBX 
    mov  RDX, RAX 
    call _ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcm 
    mov  RAX, R14 
    add  RSP, 8 
    pop  RBX 
    pop  R14 
    ret 

.L.str: 
    .zero 1 
    .size .L.str, 1 

내 경우에는두 버전 모두 strlen을 호출하지만 빈 문자열 버전은 점프를 사용하지 않고 조건부 이동 만 사용합니다 (동일한 생성자가 두 개의 다른 인수로 호출되므로). 물론 이것은 전혀 의미가 없으며 이식 가능하지 않고 전송할 수없는 관찰이지만 컴파일러가 항상 생각만큼 도움이 필요하지는 않다는 것을 보여 주기만하면됩니다. 가장 잘 보이는 코드를 작성하십시오.

+2

첫 번째 두 개는 동일하지 않으며, 심지어 IMHO 의미 론적으로도 그 중 하나에'strlen'을 호출합니다. 컴파일러가 컴파일 타임에 대체 할 수는 있지만 여전히'std :: string ()'와'std :: string ("", 0)'을 포함한다. 내용이 비어 있음을 알고 있기 때문에, 기본 생성자를 호출하는 것은 길이가 0 인'char' 배열을 전달, 계산 및 복사하는 것보다 더 낫습니다. –

+0

음, 널리 사용되는 C++ 문자열 클래스 (적어도 Qt'QString')는 " null string "은"빈 문자열 "과 구별이 가능합니다.이 질문은 왜이 질문에 대한 동기 부여의 일부입니다. – hyde

+3

@JonathanWakely : 맞습니다. 코드에서는 동일하지 않지만 의미 상으로는 ... (그리고 빈 문자열에 대한 strlen은 여전히 ​​꽤 좋습니다). 이봐,'cstr? cstr : string()'? –

1

cstr == NULL이 만족 스럽다고 가정하면 mStdString이 비어 있습니다. 첫 번째 것은 아마도 최고라고 생각합니다.

mStdStringconst 인 경우 제공하는 세 번째 옵션은 작동하지 않습니다. 중간 옵션은 C++ 11에서 "move semantics"의 이점을 누리지 만, 덜 분명하거나 최적이 아닙니다.

제 투표는 첫 번째 옵션으로 진행됩니다.

+0

"합리적인 최적화 가정"우리는 실제 이동이 필요없고 모든 복사본이 생략된다고 가정 할 수 있습니다. –

1

첫째 우선, 당신은 http://www.cplusplus.com/reference/string/string/string/에서, 맞아 :

의가 널 포인터 인 경우) [첫째, 마지막으로 지정된 범위가 유효하지 않은 N == 비영리 경우, 또는 경우, 정의되지 않은 동작이 발생합니다.

또한 NULL 포인터가 무엇을 의미하는지에 따라 다릅니다. 나는 당신을 위해 빈 문자열과 같다고 생각합니다.

제가 처음 읽었던 책이기 때문에 나는 첫 번째 책을 읽을 것입니다. 첫 번째 해결책과 두 번째는 동일합니다. 문자열이 const이면 세 번째는 작동하지 않습니다.

0

이것은 답변이 될 수는 없지만 (특히 질문을 공식화 한 경우) 특히 댓글에 적합하기에는 너무 길며 댓글에 ork이없는 코드가 있습니다. 나는 완전히 downvoted 얻을 것이고이 지위를 삭제해야한다고 생각한다. 그러나 나는 무엇인가 말할 것을 강요 당한다.

은 왜 초기화는 NULL이 될 char * 것 - 그렇다면, 당신은 호출자에게 밀어 수없는 것은 그 상황의 어떤 의미를 만들기 위해 - 같은 빈 문자열을 전달하거나 "unknown" 또는 "(null)"로 적절하게. 이 같은

, 뭔가 :

void MyClass::MyClass(const char *cstr) 
{ 
    assert(cstr != NULL); // or "throw cstr_must_not_be_null;" or some such. 
    mStdString = cstr; 
} 

(가 이니셜 라이저 목록에서이 작업을 수행하는 몇 가지 현명한 방법은 아마도,하지만 난 제대로 것을 어떻게 할 알아낼 방해 할 수 없음) .

"이것은 실제로 존재하지 않습니다"와 다른 방법으로 문자열 매개 변수에 대한 입력으로 NULL을 원하지 않습니다. 실제로 복제하려고하는 경우 boolean이 있어야합니다. 말하자면 "존재하지 않는다"거나 문자열이 없으면 NULL 일 수있는 std::string에 대한 포인터입니다.

+0

인수를 위해 가상 멤버 함수 호출의 결과로 일부 매개 변수의 기본값을 사용하려는 클래스가 있다고 가정합니다. 메서드를 호출하여 (호출 할 개체가 없으므로) 실제로 기본값을 설정할 수 없으며 고정 된 값 (빈 문자열)을 전달하면 사용자가 제공 할 수있는 유효한 값이 줄어들 수 있습니다. 즉, 빈 문자열을 사용하여 기본값을 나타내면 빈 문자열을 유효한 값 자체로 사용할 수 없습니다. 이 경우에 nullptr를 사용하는 것이 적절하다고 정당화 된 것처럼 보입니다. IMHO. –

+0

나는 가상 회원 기능을 말했지만, "가상"을 용서해주십시오. 동일한 클래스 계층 구조의 생성자 내부에서 가상 함수를 호출 할 때의 의미를 이해합니다. 내 의견을 위해 비회원 멤버 함수로 충분합니다. –

관련 문제