컴파일이 이상한 코드 스 니펫을 발견했습니다.
class Car
{
public:
int speed;
};
int main()
{
int Car::*pSpeed = &Car::speed;
return 0;
}
왜 C ++이 클래스의 비 정적 데이터 멤버에 대한이 포인터를 가지고 있습니까? 실제 코드에서이 이상한 포인터를 사용하는 것은 무엇입니까 ?
답변
“멤버를 가리키는 포인터”입니다. 다음 코드는 그 사용법을 보여줍니다.
#include <iostream>
using namespace std;
class Car
{
public:
int speed;
};
int main()
{
int Car::*pSpeed = &Car::speed;
Car c1;
c1.speed = 1; // direct access
cout << "speed is " << c1.speed << endl;
c1.*pSpeed = 2; // access via pointer to member
cout << "speed is " << c1.speed << endl;
return 0;
}
에 관해서는 왜 당신이하고 싶은 것, 잘 당신에게 몇 가지 까다로운 문제를 해결할 수있는 간접 다른 수준을 제공합니다. 그러나 솔직히 말해서, 나는 내 코드에서 그것들을 사용할 필요가 없었습니다.
편집 : 회원 데이터에 대한 포인터를 설득력있게 사용한다고 생각할 수 없습니다. 멤버 함수에 대한 포인터는 플러그 가능한 아키텍처에서 사용할 수 있지만 작은 공간에서 예제를 다시 생성하면 패배합니다. 다음은 최선의 (시험되지 않은) 시도입니다-사용자가 선택한 멤버 함수를 객체에 적용하기 전에 사전 및 사후 처리를 수행하는 Apply 함수입니다.
void Apply( SomeClass * c, void (SomeClass::*func)() ) {
// do hefty pre-call processing
(c->*func)(); // call user specified function
// do hefty post-call processing
}
연산자는 함수 호출 연산자보다 우선 순위가 낮 c->*func
으므로 괄호 가 필요합니다 ->*
.
답변
이것은 내가 생각할 수있는 가장 간단한 예입니다.이 기능이 관련된 드문 경우를 전달합니다.
#include <iostream>
class bowl {
public:
int apples;
int oranges;
};
int count_fruit(bowl * begin, bowl * end, int bowl::*fruit)
{
int count = 0;
for (bowl * iterator = begin; iterator != end; ++ iterator)
count += iterator->*fruit;
return count;
}
int main()
{
bowl bowls[2] = {
{ 1, 2 },
{ 3, 5 }
};
std::cout << "I have " << count_fruit(bowls, bowls + 2, & bowl::apples) << " apples\n";
std::cout << "I have " << count_fruit(bowls, bowls + 2, & bowl::oranges) << " oranges\n";
return 0;
}
여기서 주목할 것은 count_fruit에 전달 된 포인터입니다. 이렇게하면 별도의 count_apples 및 count_oranges 함수를 작성하지 않아도됩니다.
답변
다른 응용 프로그램은 침입 목록입니다. 요소 유형은 목록에 다음 / 이전 포인터가 무엇인지 알려줄 수 있습니다. 따라서 목록은 하드 코딩 된 이름을 사용하지 않지만 기존 포인터를 계속 사용할 수 있습니다.
// say this is some existing structure. And we want to use
// a list. We can tell it that the next pointer
// is apple::next.
struct apple {
int data;
apple * next;
};
// simple example of a minimal intrusive list. Could specify the
// member pointer as template argument too, if we wanted:
// template<typename E, E *E::*next_ptr>
template<typename E>
struct List {
List(E *E::*next_ptr):head(0), next_ptr(next_ptr) { }
void add(E &e) {
// access its next pointer by the member pointer
e.*next_ptr = head;
head = &e;
}
E * head;
E *E::*next_ptr;
};
int main() {
List<apple> lst(&apple::next);
apple a;
lst.add(a);
}
답변
신호 처리 / 제어 시스템에서 현재 작업중인 실제 예는 다음과 같습니다.
수집중인 데이터를 나타내는 구조가 있다고 가정하십시오.
struct Sample {
time_t time;
double value1;
double value2;
double value3;
};
이제 벡터에 벡터를 넣었다고 가정하십시오.
std::vector<Sample> samples;
... fill the vector ...
이제 샘플 범위에 걸쳐 변수 중 하나의 함수 (예 : 평균)를 계산하고이 평균 계산을 함수로 고려하려고한다고 가정합니다. 포인터-투-멤버를 사용하면 다음이 쉬워집니다.
double Mean(std::vector<Sample>::const_iterator begin,
std::vector<Sample>::const_iterator end,
double Sample::* var)
{
float mean = 0;
int samples = 0;
for(; begin != end; begin++) {
const Sample& s = *begin;
mean += s.*var;
samples++;
}
mean /= samples;
return mean;
}
...
double mean = Mean(samples.begin(), samples.end(), &Sample::value2);
보다 간결한 템플릿 기능 접근을 위해 2016/08/05를 편집 함
물론, 순방향 반복자와 그에 더하여 size_t로 나누기를 지원하는 모든 값 유형에 대한 평균을 계산하도록 템플릿을 구성 할 수 있습니다.
template<typename Titer, typename S>
S mean(Titer begin, const Titer& end, S std::iterator_traits<Titer>::value_type::* var) {
using T = typename std::iterator_traits<Titer>::value_type;
S sum = 0;
size_t samples = 0;
for( ; begin != end ; ++begin ) {
const T& s = *begin;
sum += s.*var;
samples++;
}
return sum / samples;
}
struct Sample {
double x;
}
std::vector<Sample> samples { {1.0}, {2.0}, {3.0} };
double m = mean(samples.begin(), samples.end(), &Sample::x);
편집-위 코드는 성능에 영향을 미칩니다
곧 알게 된 위 코드는 성능에 심각한 영향을 미칩니다. 요약은 시계열에 대한 요약 통계를 계산하거나 FFT 등을 계산하는 경우 각 변수의 값을 연속적으로 메모리에 저장해야한다는 것입니다. 그렇지 않으면 시리즈를 반복하면 검색된 모든 값에 대해 캐시 누락이 발생합니다.
이 코드의 성능을 고려하십시오.
struct Sample {
float w, x, y, z;
};
std::vector<Sample> series = ...;
float sum = 0;
int samples = 0;
for(auto it = series.begin(); it != series.end(); it++) {
sum += *it.x;
samples++;
}
float mean = sum / samples;
많은 아키텍처에서 하나의 인스턴스가 Sample
캐시 라인을 채 웁니다. 따라서 루프가 반복 될 때마다 하나의 샘플이 메모리에서 캐시로 가져옵니다. 캐시 라인에서 4 바이트를 사용하고 나머지는 버리고 다음 반복시 또 다른 캐시 누락, 메모리 액세스 등이 발생합니다.
이 작업을 수행하는 것이 훨씬 좋습니다.
struct Samples {
std::vector<float> w, x, y, z;
};
Samples series = ...;
float sum = 0;
float samples = 0;
for(auto it = series.x.begin(); it != series.x.end(); it++) {
sum += *it;
samples++;
}
float mean = sum / samples;
이제 첫 x 값이 메모리에서로드되면 다음 3 개도 캐시에로드됩니다 (적절한 정렬을 가정 함). 이는 다음 세 번의 반복에로드 된 값이 필요하지 않음을 의미합니다.
위의 알고리즘은 예를 들어 SSE2 아키텍처에서 SIMD 명령을 사용하여 약간 더 개선 될 수 있습니다. 그러나 값이 모두 메모리에서 연속적이며 단일 명령을 사용하여 4 개의 샘플을 함께로드 할 수있는 경우 (이 이후의 SSE 버전에서 더 많은 경우) 훨씬 더 효과적입니다.
YMMV-알고리즘에 맞게 데이터 구조를 설계하십시오.
답변
나중에에,이 멤버를 액세스 할 수 있는 경우 :
int main()
{
int Car::*pSpeed = &Car::speed;
Car myCar;
Car yourCar;
int mySpeed = myCar.*pSpeed;
int yourSpeed = yourCar.*pSpeed;
assert(mySpeed > yourSpeed); // ;-)
return 0;
}
인스턴스를 호출하려면 인스턴스가 필요하므로 대리자처럼 작동하지 않습니다.
그것은 거의 사용되지 않으며, 일년 내내 한두 번 필요할 수도 있습니다.
일반적으로 인터페이스 (예 : C ++의 순수 기본 클래스)를 사용하는 것이 더 나은 디자인 선택입니다.
답변
IBM 은이를 사용하는 방법에 대한 추가 문서를 가지고 있습니다. 간단히 말해서 포인터를 클래스의 오프셋으로 사용하고 있습니다. 참조하는 클래스와 별도로 이러한 포인터를 사용할 수 없습니다.
int Car::*pSpeed = &Car::speed;
Car mycar;
mycar.*pSpeed = 65;
다소 모호한 것처럼 보이지만 하나의 가능한 응용 프로그램은 일반 데이터를 여러 다른 객체 유형으로 역 직렬화하기위한 코드를 작성하려고 할 때 코드가 전혀 알지 못하는 객체 유형을 처리해야하는 경우입니다 (예 : 코드는 라이브러리에서 역 직렬화하는 개체는 라이브러리의 사용자가 만든 것입니다). 멤버 포인터는 C 구조체에 대해 할 수있는 방식이없는 void * 트릭에 의존하지 않고 개별 데이터 멤버 오프셋을 참조하는 일반적이고 반 가독성이 있습니다.
답변
멤버 변수와 함수를 균일 한 방식으로 바인딩 할 수 있습니다. 다음은 Car 클래스의 예입니다. 더 일반적으로 사용되는 바인딩 될 std::pair::first
및 ::second
지도에 STL 알고리즘과 부스트에 사용하는 경우.
#include <list>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
class Car {
public:
Car(int s): speed(s) {}
void drive() {
std::cout << "Driving at " << speed << " km/h" << std::endl;
}
int speed;
};
int main() {
using namespace std;
using namespace boost::lambda;
list<Car> l;
l.push_back(Car(10));
l.push_back(Car(140));
l.push_back(Car(130));
l.push_back(Car(60));
// Speeding cars
list<Car> s;
// Binding a value to a member variable.
// Find all cars with speed over 60 km/h.
remove_copy_if(l.begin(), l.end(),
back_inserter(s),
bind(&Car::speed, _1) <= 60);
// Binding a value to a member function.
// Call a function on each car.
for_each(s.begin(), s.end(), bind(&Car::drive, _1));
return 0;
}