问题
I wondered whether it is possible in C++ to do the same stuff like in Java. That means something like this:
public class A {
private A a1;
private A a2;
A getA1(){
return a1;
}
A getA2(){
return a2;
}
void setA1(A a1){
this.a1 = a1;
}
void setA2(A a2){
this.a2 = a2;
}
}
Now I want the same thing or a workaround in C++.
回答1:
Yes, it's doable in C++. But the syntax would be a little different:
this->
instead ofthis.
private:
/public:
instead ofprivate
/public
per memberremember to have
;
at the end of the classA*
as member (orstd::uniqe_ptr<A>
orstd::shared_ptr<A>
orstd::weak_ptr<A>
).
Items 1-3 are merely syntax. Item 4 is an essential difference between Java and C++:
In Java an object variable is a reference to the object while in C++ an object variable is a value. This is why you can't hold in C++ a direct member of yourself, as is, the size of the object would be infinite (A holding an actual value of A, holding an actual value of A, ... recursively).
In Java when A holds an A, it just holds a reference to the other A (yes, you can still access recursively the referenced A, but it is not part of your size, you just hold a reference to it, it is stored elsewhere in memory. The addition to your size is just the size of a reference).
You can achieve similar semantics in C++ with reference variables or pointers, by adding
&
for a reference or*
for a pointer:A& a2 = a1; // a2 is a reference to A, assigned with a reference to a1 // note that a1 above is assumed to be also of type A& A* a2 = a1; // a2 is a pointer to A, assigned with the address stored in a1 // note that a1 above is assumed to be also of type A*
Java Garbage Collector reclaims unused memory while in C++ the programmer needs to handle that, possibly with C++ tools such as smart pointers.
Java Garbage Collector reclaims unused memory via Trace by Reachability, C++ smart pointers are based on scope lifetime. Additionally, C++
shared_ptr
is based on reference counting which has its advantages, but is subject to reference cycles possible leak of memory, which should be avoided with proper design of your code.
The C++ version of "holding myself" may look like any of the below (or variations of them), depending on the exact need:
Option 1 - A is holding but not owning a1 and a2
class A {
A* a1 = nullptr;
A* a2 = nullptr;
public:
A* getA1(){
return a1;
}
A* getA2(){
return a2;
}
void setA1(A* a1){
this->a1 = a1;
}
void setA2(A* a2){
this->a2 = a2;
}
};
Option 2 - A is owning a1 and a2 as a unique resource
class A {
std::unique_ptr<A> a1 = nullptr;
std::unique_ptr<A> a2 = nullptr;
public:
A* getA1(){
return a1.get();
}
A* getA2(){
return a2.get();
}
void setA1(std::unique_ptr<A> a1){
this->a1 = std::move(a1);
}
void setA2(std::unique_ptr<A> a2){
this->a2 = std::move(a2);
}
};
Option 3 - A is holding a1 and a2 as a shared resource*
* need to make sure you avoid cyclic ownership leak.
class A {
std::shared_ptr<A> a1 = nullptr;
std::shared_ptr<A> a2 = nullptr;
public:
auto getA1(){
return a1;
}
auto getA2(){
return a2;
}
void setA1(std::shared_ptr<A> a1){
this->a1 = a1;
}
void setA2(std::shared_ptr<A> a2){
this->a2 = a2;
}
};
Option 4 - A is holding weak pointers to a1 and a2*
* the option of std::weak_ptr
is relevant in case of possible cyclic dependency, a1 and a2 are owned elsewhere and might not be alive.
class A {
std::weak_ptr<A> a1 = nullptr;
std::weak_ptr<A> a2 = nullptr;
public:
std::shared_ptr<A> getA1(){
return a1.lock();
}
std::shared_ptr<A> getA2(){
return a2.lock();
}
void setA1(std::shared_ptr<A> a1){
this->a1 = a1;
}
void setA2(std::shared_ptr<A> a2){
this->a2 = a2;
}
};
Option 4 code example: http://coliru.stacked-crooked.com/a/92d6004280fdc147
Note that using A&
(reference to A) as a member, is not an option, as in C++ reference variables are stronger than Catholic wedding, they're for the lifetime of the variable without any way to reassign to another reference. And they must be assigned to a valid reference when born.
However, if a1
and a2
are known when the object is born, never change and stay alive for the duration of the object's lifetime, then the following option is also possible:
Option 5 - A is holding references to a1 and a2*
* this option is relevant only if the reference to a1
and a2
can be set upon creation, never changes and stays alive for the duration of the object's lifetime.
class A {
A& a1;
A& a2;
public:
A(A& a1, A& a2): a1(a1), a2(a2) {}
A& getA1(){
return a1;
}
A& getA2(){
return a2;
}
};
来源:https://stackoverflow.com/questions/63365537/c-instance-of-same-class