如何在C++17中捕获对象属性的破坏性赋值事件并监听更新?
Alright, let's figure out how to mirror that JavaScript property listening trick in C17! JavaScript has handy built-in tools like Object.defineProperties for this, but C takes a more explicit approach—we'll use either classic getters/setters or a proxy class to replicate that "listen for property updates" behavior. Here are two solid approaches:
Approach 1: Classic Getters & Setters (Simple & Straightforward)
This is the most common way to handle property encapsulation and listening in C++. We make the actual property private, then expose public getter and setter methods. The setter is where we hook in our "destructive assignment" logic.
#include <iostream> class MyObject { private: // Private backing storage for the property int t_ = 0; public: // Getter: returns the current value (matches JS's get()) int getT() const { return t_; } // Setter: handles the update and triggers our listening logic (matches JS's set(newT)) void setT(int newT) { // Add your custom logic here—this runs every time the property is updated std::cout << "Detected destructive assignment! t changing from " << t_ << " to " << newT << "\n"; t_ = newT; } }; int main() { MyObject obj; obj.setT(5); // Triggers our listening logic std::cout << "Current t value: " << obj.getT() << "\n"; obj.setT(10); // Triggers again return 0; }
Why this works:
By keeping the actual t_ private, we force all external code to use setT() to modify the value. This lets us intercept every update and run our custom logic—just like the JS setter.
Approach 2: Proxy Class (JS-like Direct Assignment)
If you want syntax that feels more like JavaScript (where you can write obj.t = 5 instead of obj.setT(5)), a proxy class is the way to go. This wraps the property and overrides the assignment operator to trigger our listening code.
#include <iostream> #include <functional> // Template proxy class to handle property access and updates template <typename T> class PropertyProxy { private: T value_; std::function<void(const T& oldVal, const T& newVal)> onUpdate_; public: // Constructor: takes initial value and the callback to run on assignment PropertyProxy(T initialVal, std::function<void(const T&, const T&)> onUpdate) : value_(initialVal), onUpdate_(std::move(onUpdate)) {} // Override assignment operator to trigger our logic PropertyProxy& operator=(const T& newVal) { onUpdate_(value_, newVal); // Run our listening code value_ = newVal; return *this; } // Implicit conversion to the underlying type for easy reading operator T() const { return value_; } }; // Our object class with the proxied property class MyObject { public: PropertyProxy<int> t; MyObject() : t(0, [](const int& oldVal, const int& newVal) { // Custom listening logic here std::cout << "t is being updated from " << oldVal << " to " << newVal << "\n"; }) {} }; int main() { MyObject obj; obj.t = 5; // Direct assignment—triggers our logic just like JS std::cout << "Current t: " << static_cast<int>(obj.t) << "\n"; obj.t = 15; // Triggers again return 0; }
Why this works:
The PropertyProxy class acts as a middleman for the property. When you assign to obj.t, you're actually calling the proxy's operator=, which runs our listening callback before updating the value. The implicit conversion lets us read obj.t directly too, just like a regular member variable.
Quick Notes for C++17
- C++ is statically typed, so unlike JavaScript, you can't dynamically add properties to an object at runtime unless you use something like
std::unordered_map<std::string, std::any>(though this sacrifices type safety). - Both approaches work perfectly in C17—no fancy C20 features needed here.
内容的提问来源于stack exchange,提问作者user1028880

