Наличие общедоступных свойств в классе С++

Как мне получить свойства в классе С++, как в классе С#.

Я не хочу иметь методы getter и setter.

Ответ 1

Вы можете использовать решение, подобное предложению Джона, но сохраняя обычную семантику С++, используя перегрузку оператора. Я немного изменил код Джона следующим образом (объяснения следуют за кодом):

#include <iostream>

template<typename T>
class Accessor {
public:
    explicit Accessor(const T& data) : value(data) {}

    Accessor& operator=(const T& data) { value = data; return *this; }
    Accessor& operator=(const Accessor& other) { this->value = other.value; return *this; }
    operator T() const { return value; }
    operator T&() { return value; }

private:
    Accessor(const Accessor&);


    T value;

};

struct Point {
    Point(int a = 0, int b = 0) : x(a), y(b) {}
    Accessor<int> x;
    Accessor<int> y;
};

int main() {
    Point p;
    p.x = 10;
    p.y = 20;
    p.x++;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 15;
    std::cout << p.x << "," << p.y << std::endl;

    return 0;
}

Мы перегружаем operator=, чтобы сохранить обычный синтаксис присваивания вместо синтаксиса, подобного функции. Мы используем оператор литья как "геттер". Нам нужна вторая версия operator=, чтобы разрешить присвоение второго рода в main().

Теперь вы можете добавить в указатели функций конструктора Accessor, или лучше - функторы - вызвать как getters/seters каким-либо образом, кажется вам правильным. В следующем примере предполагается, что функция setter возвращает bool, чтобы передать согласие на установку нового значения, и геттер может просто изменить его на выходе:

#include <iostream>
#include <functional>
#include <cmath>

template<typename T>
class MySetter {
public:
    bool operator()(const T& data)
    {
        return (data <= 20 ? true : false);
    }
};

template<typename T>
class MyGetter {
public:
    T operator()(const T& data)
    {
        return round(data, 2);
    }

private:
    double cint(double x) {
        double dummy;
        if (modf(x,&dummy) >= 0.5) {
            return (x >= 0 ? ceil(x) : floor(x));
        } else {
            return (x < 0 ? ceil(x) : floor(x));
        }
    }

    double round(double r, int places) {
        double off = pow(10.0L, places);
        return cint(r*off)/off;
    }
};

template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
public:
    explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}

    Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
    Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
    operator T() const { value = getter(value); return value;}
    operator T&() { value = getter(value); return value; }

private:
    Accessor(const Accessor&);

    T value;

    G getter;
    S setter;

};

struct Point {
    Point(double a = 0, double b = 0) : x(a), y(b) {}
    Accessor<double> x;
    Accessor<double> y;
};

int main() {
    Point p;
    p.x = 10.712;
    p.y = 20.3456;
    p.x+=1;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 15.6426;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 25.85426;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 19.8425;
    p.y+=1;
    std::cout << p.x << "," << p.y << std::endl;

    return 0;
}

Однако, как показывает последняя строка, у нее есть ошибка. Оператор литья, возвращающий T & позволяет пользователям обойти сеттер, так как он дает им доступ к частному значению. Один из способов решить эту проблему - реализовать все операторы, которые вы хотите предоставить своему Аксессуру. Например, в следующем коде я использовал оператор + =, и поскольку я удалил оператор переноса, возвращающий ссылку, мне пришлось реализовать operator+=:

#include <iostream>
#include <functional>
#include <cmath>

template<typename T>
class MySetter {
public:
    bool operator()(const T& data) const {
        return (data <= 20 ? true : false);
    }
};

template<typename T>
class MyGetter {
public:
    T operator() (const T& data) const {
        return round(data, 2);
    }

private:
    double cint(double x) const {
        double dummy;
        if (modf(x,&dummy) >= 0.5) {
            return (x >= 0 ? ceil(x) : floor(x));
        } else {
            return (x < 0 ? ceil(x) : floor(x));
        }
    }

    double round(double r, int places) const {
        double off = pow(10.0L, places);
        return cint(r*off)/off;
    }
};

template<typename T, typename G = MyGetter<T>, typename S = MySetter<T>>
class Accessor {
private:
public:
    explicit Accessor(const T& data, const G& g = G(), const S& s = S()) : value(data), getter(g), setter(s) {}

    Accessor& operator=(const T& data) { if (setter(data)) value = data; return *this; }
    Accessor& operator=(const Accessor& other) { if (setter(other.value)) this->value = other.value; return *this; }
    operator T() const { return getter(value);}

    Accessor& operator+=(const T& data) { if (setter(value+data)) value += data; return *this; }

private:
    Accessor(const Accessor&);

    T value;

    G getter;
    S setter;

};

struct Point {
    Point(double a = 0, double b = 0) : x(a), y(b) {}
    Accessor<double> x;
    Accessor<double> y;
};

int main() {
    Point p;
    p.x = 10.712;
    p.y = 20.3456;
    p.x+=1;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 15.6426;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 25.85426;
    std::cout << p.x << "," << p.y << std::endl;

    p.x = p.y = 19.8425;
    p.y+=1;
    std::cout << p.x << "," << p.y << std::endl;

    return 0;
}

Вам нужно будет реализовать все операторы, которые вы собираетесь использовать.

Ответ 2

Для поведения такого типа я использую шаблонный метааксесуатор. Здесь очень упрощен для типов POD:

template<class T>
struct accessor {

    explicit accessor(const T& data) : value(data) {}
    T operator()() const { return value; }
    T& operator()() { return value; }
    void operator()(const T& data) { value = data; }

private:

    accessor(const accessor&);
    accessor& operator=(const accessor&);
    T value;

};

Типичное использование:

struct point {
    point(int a = 0, int b = 0) : x(a), y(b) {}
    accessor<int> x;
    accessor<int> y;
};

point p;
p.x(10);
p.y(20);
p.x()++;
std::cout << p.x();

Компилятор обычно строит эти вызовы, если вы правильно настроите настройки и включили оптимизацию. Это не более узкое место в производительности, чем использование реальных геттеров и сеттеров, независимо от того, какие оптимизации происходят. Тривиально расширять это, чтобы автоматически поддерживать не-POD или перечисленные типы или разрешать регистрацию обратных вызовов для всякий раз, когда считываются или записываются данные.

Изменить. Если вы не хотите использовать круглые скобки, вы всегда можете определить operator=() и неявный оператор литья. Здесь версия, которая делает именно это, а также добавляет базовую поддержку "обратного вызова":

Дальнейшее редактирование: Ладно, полностью пропустил, что кто-то уже сделал исправленную версию моего кода. Вздох.

Ответ 3

Здесь реализация PoC, которую я сделал некоторое время назад, прекрасно работает, за исключением того, что вам нужно установить что-то в конструкторе, чтобы она работала хорошо и плавно.

http://www.codef00.com/code/Property.h

Здесь пример использования:

#include <iostream>
#include "Property.h"


class TestClass {
public:
    // make sure to initialize the properties with pointers to the object
    // which owns the property
    TestClass() : m_Prop1(0), m_Prop3(0.5), prop1(this), prop2(this), prop3(this) {
    }

private:
    int getProp1() const {
        return m_Prop1;
    }

    void setProp1(int value) {
        m_Prop1 = value;
    }

    int getProp2() const {
        return 1234;
    }

    void setProp3(double value) {
        m_Prop3 = value;
    }

    int m_Prop1;
    double m_Prop3;

public:
    PropertyRW<int, TestClass, &TestClass::getProp1, &TestClass::setProp1> prop1;
    PropertyRO<int, TestClass, &TestClass::getProp2> prop2;
    PropertyWO<double, TestClass, &TestClass::setProp3> prop3;
};

и некоторое использование этого класса...

int main() {
    unsigned int a;
    TestClass t;
    t.prop1 = 10;
    a = t.prop1;
    t.prop3 = 5;
    a = t.prop2;
    std::cout << a << std::endl;
    return 0;
}

Есть два раздражения при таком подходе:

  • Вам нужно предоставить собственность указатель на свой собственный класс.
  • Синтаксис объявления свойства - это бит многословный, но я уверен, что могу чистить что немного с некоторыми макросами

Ответ 4

Если вам все равно, что ваш код на С++ не будет компилироваться ни с чем другим, кроме компилятора Microsoft Visual С++, вы можете использовать некоторые из нестандартных расширений компилятора.

Например, следующий код создаст свойство типа С#, называемое MyProperty.

struct MyType
{
    // This function pair may be private (for clean encapsulation)
    int get_number() const { return m_number; }
    void set_number(int number) { m_number = number; }

    __declspec(property(get=get_number, put=set_number)) int MyProperty;
private:
    int m_number:
}

int main()
{
    MyType m;
    m.MyProperty = 100;
    return m.MyProperty;
}

Дополнительную информацию об этом расширении для Microsoft можно найти здесь.

Ответ 5

Нет. С++ не поддерживает такие свойства, как С#. Если вы хотите, чтобы код работал на set/get, это должен быть метод.

Ответ 6

Свойства не поддерживаются в С++, но вы можете их реализовать:
1) Используя шаблоны
2) С помощью расширения языка и написания препроцессора пользовательского кода

Любой подход будет непростым, но это можно сделать.

Ответ 7

Вы можете предоставить методы get и set, имеющие похожие имена для членов данных:

class Example
{
  private:
     unsigned int x_;
     double d_;
     std::string s_s;
  public:
     unsigned int x(void) const
     { return x_;}

     void x(unsigned int new_value)
     { x_ = new_value;}

     double d(void) const
     { return d_;}
     void d(double new_value)
     { d_ = new_value;}

     const std::string& s(void) const
     { return s_;}
     void s(const std::string& new_value)
     { s_ = new_value;}
};

Хотя это близко, поскольку для каждого участника требуется использовать '()', он не соответствует точной функциональности свойств, предоставляемых Microsoft Languages.

Ближайшее совпадение свойств - объявить членов данных общедоступными.