C ++에서 생성자의 주소를 사용할 수없는 이유는 무엇입니까?


14

이것이 언어를 개념적으로 깨뜨릴 특별한 이유가 있거나 기술적으로 실현 불가능한 특정한 이유가 있습니까?

사용법은 새로운 연산자로 이루어집니다.

편집 : 나는 "새로운 운영자"와 "새로운 운영자"를 똑바로 세우고 직접적으로 희망을 포기할 것입니다.

질문의 요점은 왜 생성자가 특별한가 ? 언어 사양에 따라 합법적이지만 반드시 도덕적 인 것은 아니라는 점을 명심하십시오. 합법적 인 것은 일반적으로 나머지 언어와 논리적으로 일치하는 것, 간단하고 간결한 것 및 컴파일러가 구현할 수있는 것에 의해 알 수 있습니다. 이러한 요소들을 평가할 때 표준위원회의 가능한 이론적 근거는 고의적이고 흥미 롭기 때문에 문제입니다.


생성자의 주소를 가져 오는 데 문제가 없지만 유형을 전달할 수는 없습니다. 템플릿이 그렇게 할 수 있습니다.
Euphoric

함수에 대한 인수로 지정 될 생성자를 사용하여 객체를 생성하려는 함수 템플릿이있는 경우 어떻게합니까?
Praxeolitic

1
내가 생각할 수있는 예에 대한 대안이 있지만 여전히 생성자가 특별한 이유는 무엇입니까? 대부분의 프로그래밍 언어에서 사용하지 않을 것들이 많이 있지만 이와 같은 특별한 경우에는 대개 정당성이 있습니다.
Praxeolitic

1
@RobertHarvey 팩토리 클래스를 입력하려고 할 때 문제가 발생했습니다.
Praxeolitic 2016 년

1
나는 C ++ (11) 궁금 std::make_unique하고 std::make_shared적절하게이 질문에 대한 기본 실천 의욕을 해결할 수 있습니다. 이들은 템플릿 메소드이므로 입력 인수를 생성자에 캡처 한 다음 실제 생성자에 전달해야합니다.
rwong

답변:


10

포인터 대 멤버 함수는 동일한 서명을 가진 멤버 함수가 둘 이상인 경우에만 의미가 있습니다. 그렇지 않으면 포인터에 대해 가능한 값이 하나만 있습니다. 그러나 C ++에서는 동일한 클래스의 다른 생성자가 다른 서명을 가져야하기 때문에 생성자는 불가능합니다.

Stroustrup의 대안은 생성자가 클래스 이름과 다른 이름을 가질 수있는 C ++의 구문을 선택하는 것이었지만 기존의 ctor 구문의 매우 우아한 측면을 막고 언어를 더 복잡하게 만들었습니다. 저에게는 ctor에서 다른 init함수 (포인터-투-멤버가 될 수있는 일반적인 멤버 함수)로 오브젝트의 초기화를 "아웃소싱"하여 쉽게 시뮬레이션 할 수있는 거의 필요한 기능을 허용하지 않는 높은 가격 인 것 같습니다. 만들어진).


2
아직도, 왜 방지 memcpy(buffer, (&std::string)(int, char), size)합니까? (아마도 매우
코 셔링은 좋지 않지만

3
미안하지만 당신이 쓴 것은 말이되지 않습니다. 생성자를 가리키는 멤버에 대한 포인터를 갖는 데 아무런 문제가 없습니다. 또한 소스에 링크하지 않고 무언가를 인용 한 것처럼 들립니다.
BЈовић

1
@ThomasEding : 그 진술이 정확히 무엇을 기대합니까? 문자열 ctor의 어셈블리 코드를 복사합니까? "크기"는 어떻게 결정됩니까 (표준 멤버 함수와 동등한 것을 시도하더라도)?
Doc Brown

무료 함수 포인터의 주소가 주어진 것과 똑같은 일을 할 것으로 기대합니다 memcpy(buffer, strlen, size). 아마도 그것은 집회를 모방 할 것이지만 누가 아는가. 충돌없이 코드를 호출 할 수 있는지 여부는 사용하는 컴파일러에 대한 지식이 필요합니다. 크기를 결정할 때도 마찬가지입니다. 플랫폼에 따라 크게 다르지만 이식성이없는 많은 C ++ 구문이 프로덕션 코드에 사용됩니다. 나는 그것을 금지 할 이유가 없다.
Thomas Eding 2016

@ThomasEding : 적합한 C ++ 컴파일러는 마치 함수 포인터가 데이터 포인터 인 것처럼 함수 포인터에 액세스하려고 할 때 진단을 제공해야합니다. 적합하지 않은 C ++ 컴파일러는 무엇이든 할 수 있지만 생성자에 액세스하는 비 C ++ 방법도 제공 할 수 있습니다. 그렇기 때문에 코드를 준수하지 않는 기능을 C ++에 추가 할 필요는 없습니다.
Bart van Ingen Schenau

5

생성자는 개체가 아직 없을 때 호출하는 함수이므로 멤버 함수가 될 수 없습니다. 정적 일 수 있습니다.

메모리가 할당 된 후가 완전히 초기화되기 전에 생성자가 실제로 this 포인터로 호출됩니다. 결과적으로 생성자는 여러 특권 기능을 갖습니다.

생성자에 대한 포인터가 있다면 정적 포인터, 팩토리 함수와 같은 것이거나 메모리 할당 직후에 호출되는 무언가에 대한 특수 포인터 여야합니다. 일반적인 멤버 함수일 수 없으며 여전히 생성자로 작동합니다.

염두에 두어야 할 유용한 목적은 새로운 연산자에 전달하여 사용할 생성자를 간접적으로 지정할 수있는 특수한 종류의 포인터입니다. 나는 그것이 유용 할 수는 있지만, 새로운 구문이 필요하고 아마도 대답은 다음과 같습니다. 그들은 그것에 대해 생각했으며 노력할 가치가 없었습니다.

일반적인 초기화 코드를 리팩토링하려면 일반적인 메모리 함수로 충분합니다. 그 중 하나에 대한 포인터를 얻을 수 있습니다.


이것은 가장 정답 인 것 같습니다. 나는 몇 년 전부터 새로운 운영자와“새로운 운영자”의 내부 활동에 관한 기사를 회상합니다. 연산자 new ()는 공간을 할당합니다. 새로운 연산자는 할당 된 공간으로 생성자를 호출합니다. 생성자를 호출하는 데는 공간이 필요하므로 생성자의 주소를 취하는 것이 "특별"합니다. 이와 같이 생성자를 호출 할 수있는 액세스 권한은 새로운 배치입니다.
Bill Door

1
"존재"라는 단어는 개체가 주소를 가질 수 있고 메모리를 할당했지만 초기화 할 수 없다는 세부 사항을 모호하게합니다. 멤버 함수 여부에 관계 없이이 포인터를 가져 오면 함수가 멤버 함수와 명확하게 연관되어 있기 때문에 함수를 멤버 함수로 생각합니다 (초기화되지 않은 경우에도). 즉, 대답은 좋은 지적입니다. 생성자는 초기화되지 않은 객체에서 호출 할 수있는 유일한 멤버 함수입니다.
Praxeolitic

분명히, 그들은 "특별한 멤버 함수"라는 명칭을 가지고 있습니다. C ++ 11 표준의 12 절 : "기본 생성자 (12.1), 복사 생성자 및 복사 할당 연산자 (12.8), 이동 생성자 및 이동 할당 연산자 (12.8) 및 소멸자 (12.4)는 특수 멤버 함수 입니다."
Praxeolitic

12.1 : "생성자는 가상 (10.3) 또는 정적 (9.4) 이 아니어야 합니다." (내 강조)
Praxeolitic

1
사실 디버그 기호로 컴파일하고 스택 추적을 찾으면 실제로 생성자에 대한 포인터가 있습니다. 내가 결코 할 수 없었던 것은이 포인터를 얻기위한 구문을 찾는 것입니다 ( &A::A내가 시도한 컴파일러에서는 작동하지 않습니다)
alfC

-3

이는 반환 유형의 생성자가 아니기 때문에 메모리에 생성자를위한 공간을 예약하지 않기 때문입니다. 선언하는 동안 변수가있는 경우처럼 u. 예를 들어 : u가 간단한 변수 X를 쓰면 컴파일러는이의 의미를 이해하지 못하기 때문에 오류를 생성합니다. 그러나 Int x를 쓸 때; 그런 다음 컴파일러는 int type data variable이라는 것을 알게되어 변수를위한 공간을 예약합니다.

결론 :-결론은 반환 유형을 배제하여 주소를 메모리에 얻지 못한다는 결론입니다.


1
생성자의 코드는 어딘가에 있어야하기 때문에 메모리에 주소가 있어야합니다. 스택 에 여유 공간을 예약 할 필요는 없지만 메모리의 어딘가에 있어야합니다. 당신은 할 수 있습니다 값을 반환하지 않는 함수의 주소를 가지고. (void)(*fptr)()반환 값이없는 함수에 대한 포인터를 선언합니다.
Praxeolitic

2
질문의 요점을 놓쳤습니다. 원래 게시물은 생성자가 제공 한 결과가 아니라 생성자의 코드 주소를 가져 오는 것에 대해 물었습니다. 또한,이 게시판에서 "u"는 "you"를 대신 할 수 없습니다.
BobDalgleish

praxeolitic 씨, 리턴 타입을 언급하지 않으면 컴파일러가 ctor에 대한 특정 메모리 위치를 설정하지 않고 위치가 내부적으로 설정됩니다. ... 컴파일러? 틀린 경우 정답으로 저를 정정하십시오
lovish Goyal

또한 참조 변수에 대해서도 알려주십시오. 참조 변수의 주소를 가져올 수 있습니까? 그렇다면, 어떤 주소를 printf ( "% u", & (& (j))); & j = x 여기서 x = 10 인 경우 인쇄 중입니까? printf로 인쇄 된 주소와 x의 주소가 같지 않기 때문에
lovish Goyal

-4

나는 야생 추측을 할 것이다 :

C ++ 생성자와 소멸자는 전혀 함수가 아닙니다 : 매크로입니다. 객체가 생성 된 범위와 객체가 파괴 된 범위에 인라인됩니다. 결과적으로 생성 자나 소멸자가 없으며 객체는 IS입니다.

실제로 클래스의 다른 함수는 함수가 아니라 DONT가 인라인 함수 인 주소 함수를 사용하기 때문에 인라인 함수라고 생각합니다. 함수를 최적화하지 않으면 함수는 주소를 가져 가지 않은 경우에도 "아직있을 것"인 것처럼 보입니다.

C ++ "객체"의 가상 테이블은 JavaScript 객체와는 다릅니다. JavaScript 객체는 생성자를 가져와 런타임시 객체를 통해 객체를 생성 할 수 new XMLHttpRequest.constructor있지만이 객체와의 인터페이스 수단으로 사용되는 익명 함수에 대한 포인터 모음입니다. , 객체 생성 기능 제외. 그리고 객체를 "삭제"하는 것은 의미가 없습니다. 구조체를 삭제하려고하는 것과 같기 때문에 할 수 없습니다. 스택 레이블 일뿐입니다. 다른 레이블에서 원하는대로 쓸 수 있습니다. 클래스를 4 개의 정수로 사용하십시오.

/* i imagine this string gets compiled into a struct, one of which's members happens to be a const char * which is initialized to exactly your string: no function calls are made during construction. */
std::string a = "hello, world";
int *myInt = (int *)(*((void **)&a));
myInt[0] = 3;
myInt[1] = 9;
myInt[2] = 20;
myInt[3] = 300;

메모리 누수가 없으며 문제가 없습니다. 객체 인터페이스와 문자열을 위해 예약 된 많은 스택 공간을 효과적으로 낭비하지만 프로그램을 사용하지 않는 한 프로그램을 파괴하지는 않습니다. 다시 문자열로).

실제로, 이전의 가정이 맞다면 : 문자열의 전체 비용은 32 바이트와 상수 문자열 공간을 저장하는 비용입니다. 함수는 컴파일 타임에만 사용되며 인라인되고 던져 질 수 있습니다. 객체가 생성되고 사용됩니다 (구조체로 작업하고 함수 호출없이 직접 참조하는 경우 함수 점프 대신 중복 호출이 있는지 확인하지만 일반적으로 더 빠르며 공간을 덜 사용합니다). 본질적으로 함수를 호출 할 때마다 언어 디자이너가 설정 한 예외를 제외하고 컴파일러는 해당 호출을 문자로 수행하라는 명령으로 대체합니다.

요약 : C ++ 객체는 무엇인지 모릅니다. 인터페이스와의 인터페이스를위한 모든 도구는 정적으로 인라인되어 런타임시 손실됩니다. 이를 통해 데이터로 구조체를 채우는 것만 큼 효율적으로 클래스를 사용하고 함수를 전혀 호출하지 않고 해당 데이터로 직접 작업 할 수 있습니다 (이 함수는 인라인 됨).

이것은 컴파일러가이 정보를 버릴 수 없으므로 런타임 오버 헤드, 메모리 관리, 구조 호출 비용으로 유형 정보를 동적으로 유지하는 javascript뿐만 아니라 COM / ObjectiveC의 접근 방식과는 완전히 다릅니다. 동적 디스패치 결과적으로 런타임에 프로그램과 "토크 (Talk)"할 수 있으며, 실행 가능한 동안 반영 가능한 구성 요소를 사용하여 프로그램을 개발할 수 있습니다.


2
죄송하지만이 "답변"의 일부는 잘못되었거나 잘못 오도 된 것입니다. 안타깝게도 주석 공간이 너무 작아서 모두 나열 할 수는 없습니다 (대부분의 방법은 인라인되지 않으므로 가상 디스패치가 발생하지 않고 바이너리가 부풀어 오릅니다. 인라인 된 경우에도 어딘가에 주소 지정 가능한 사본이있을 수 있습니다. 최악의 경우는 스택을 손상시키고 최상의 경우는 가정에 맞지 않습니다. ...)
hoffmale

대답은 순진합니다. 나는 왜 생성자 / 소멸자를 참조 할 수 없는지에 대한 추측을 표현하고 싶었습니다. 가상 클래스의 경우 vtable이 유지되어야하며 vtable이 참조 할 수 있도록 주소 지정 가능한 코드가 메모리에 있어야합니다. 그러나 가상 클래스를 구현하지 않는 클래스는 std :: string의 경우와 같이 인라인 된 것으로 보입니다. 모든 것이 인라인되지는 않지만 메모리의 어딘가에 "익명"코드 블록에 들어 가지 않는 것 같습니다. 또한 코드가 스택을 어떻게 손상합니까? 물론 우리는 줄을 잃었지만, 그렇지 않으면 우리가 한 모든 것은 재 해석입니다.
Dmitry

메모리 위치의 내용이 실수로 수정 ​​된 경우 컴퓨터 프로그램에서 메모리 손상이 발생합니다. 이 프로그램은 의도적으로 그것을 수행하고 더 이상 해당 문자열을 사용하려고 시도하지 않으므로 손상이 없으며 스택 공간을 낭비합니다. 그러나 그렇습니다. 문자열의 불변은 더 이상 유지되지 않으며 범위가 어수선 해집니다 (끝에 스택이 복구됩니다).
Dmitry

문자열 구현에 따라 원하지 않는 바이트를 쓸 수 있습니다. 문자열이 struct { int size; const char * data; };(예상 한 것처럼) x86 컴퓨터에서 8 바이트 만 예약 한 메모리 주소에 4 * 4 바이트 = 16 바이트를 쓰므로 다른 데이터 위에 8 바이트가 기록되므로 스택이 손상 될 수 있습니다 ). 다행스럽게도 std::string일반적으로 짧은 문자열에 대한 적절한 최적화가 있으므로 주요 std 구현을 사용할 때 예제에 충분히 커야합니다.
hoffmale

@hoffmale 당신이 절대적으로 옳습니다 .4 바이트 일 수도 있고 8 일 수도 있고 1 바이트 일 수도 있습니다. 그러나 문자열의 크기를 알고 나면 현재 범위에서 메모리가 스택에 있다는 것을 알고 원하는대로 사용할 수 있습니다. 내 요점은 구조를 알고 있다면 클래스를 IUnknown의 vtable의 일부로 클래스를 식별하는 uuid가있는 COM 객체와 달리 클래스에 대한 정보와 무관하게 압축되어 있다는 것입니다. 결과적으로 컴파일러는 인라인 또는 맹 글링 된 정적 함수를 통해이 데이터에 직접 액세스합니다.
Dmitry
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.