C++库避免包含子类

huangapple go评论64阅读模式
英文:

C++ library avoid including sub classes

问题

Let's say we have a library that has a class "Car" which has some private members of class "Wheel".

我们有一个库,其中有一个名为“Car”的类,它有一些私有成员属于类“Wheel”。

We want the user to be able to create a "Car" and use the "Drive" function(ality) of it. This will also turn the wheels.

我们希望用户能够创建“Car”并使用其“Drive”功能,这将同时转动车轮。

However, we want to avoid the user to create a "Wheel" by itself and turn it w/o the use of a car. To avoid them using the library improperly.

但是,我们希望避免用户单独创建“Wheel”并在没有使用车辆的情况下转动它,以防止他们不正确地使用库。

See example code below, where the main.cpp is the user code and the rest is the library.

请参阅下面的示例代码,其中main.cpp是用户代码,其余部分是库。


main.cpp

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

int main()
{
    std::cout << "Hello World" << std::endl;
    Car myCar = Car();
    myCar.drive();
    
    //This should not be possible!
    Wheel someWheel = Wheel();
    someWheel.rotate();
    return 0;
}

car.h

class Wheel;//Forward declaration (to avoid '#include "wheel.h"', which would make it accessible in main.cpp.
class Car {
public:
    Car();
    void drive();
private:
    Wheel leftWheel;//Error, requires a pointer
};

car.cpp

#include <iostream>
#include "car.h"
#include "wheel.h"

Car::Car(){
    leftWheel = new Wheel();
};
void Car::drive(){
    std::cout << "Vroom!" << std::endl;
    leftWheel.rotate();
}

wheel.h

class Wheel {
public:
    Wheel();
    void rotate();
};

wheel.cpp

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

Wheel::Wheel(){
};
void Wheel::rotate(){
    std::cout << "Wheel: *Rotates*" << std::endl;    
}

We've found that by including the header file in the Car.cpp, we can achieve this. However, when we make a forward declaration of 'Wheel', we can only use a pointer to Wheel as a private member from Car.

我们发现,通过在Car.cpp中包含头文件,我们可以实现这一点。但是,当我们对“Wheel”进行前向声明时,我们只能将Wheel作为Car的私有成员使用指针。

Otherwise it leads to:

否则会导致:

car.h:12:11: error: field ‘leftWheel’ has incomplete type ‘Wheel’

So my questions are:

所以我的问题是:

  • Should we at all hide 'Wheel' from the user, for his own 'safety'? Or is there a stronger reason not to do it?

    • If so, can we achieve this without the use of a pointer
      • If not, do we have to explicitly make a deconstructor that deletes the instance from memory?
  • 我们是否应该完全隐藏“Wheel”对用户来说,出于他自己的“安全”?还是有更强烈的原因不这样做?

    • 如果是这样,是否可以在不使用指针的情况下实现这一点?
      • 如果不行,是否必须显式制作一个析构函数来删除内存中的实例?
英文:

Let's say we have a library that has a class "Car" which has some private members of class "Wheel".

We want the user to be able to create a "Car" and use the "Drive" function(ality) of it.
This will also turn the wheels.

However, we want to avoid the user to create a "Wheel" by itself and turn it w/o the use of a car. To avoid them using the library improperly.

See example code below, where the main.cpp is the user code and the rest is the library.


main.cpp

#include &lt;iostream&gt;
#include &quot;car.h&quot;

int main()
{
    std::cout &lt;&lt; &quot;Hello World&quot; &lt;&lt; std::endl;
    Car myCar = Car();
    myCar.drive();
    
    //This should not be possible!
    Wheel someWheel = Wheel();
    someWheel.rotate();
    return 0;
}

car.h

class Wheel;//Forward declaration (to avoid &#39;#include &quot;wheel.h&quot;&#39;, which would make it accessible in main.cpp.
class Car {
public:
    Car();
    void drive();
private:
    Wheel leftWheel;//Error, requires a pointer
};

car.cpp

#include &lt;iostream&gt;
#include &quot;car.h&quot;
#include &quot;wheel.h&quot;

Car::Car(){
    leftWheel = new Wheel();
};
void Car::drive(){
    std::cout &lt;&lt; &quot;Vroom!&quot;  &lt;&lt; std::endl;
    leftWheel.rotate();
}

wheel.h

class Wheel {
public:
    Wheel();
    void rotate();
};

wheel.cpp

#include &lt;iostream&gt;
#include &quot;wheel.h&quot;

Wheel::Wheel(){
};
void Wheel::rotate(){
    std::cout &lt;&lt; &quot;Wheel: *Rotates*&quot;  &lt;&lt; std::endl;    
}

We've found that by including the header file in the Car.cpp, we can achieve this.
However, when we make a forward declaration of 'Wheel', we can only use a pointer to Wheel as a private member from Car.

Otherwise it leads to:

> car.h:12:11: error: field ‘leftWheel’ has incomplete type ‘Wheel’

So my questions are:

  • Should we at all hide 'Wheel' from the user, for his own 'safety'? Or is there a stronger reason not to do it?
    • If so, can we achieve this without the use of a pointer
      • If not, do we have to explicitly make a deconstructor that deletes the instance from memory?

答案1

得分: 2

根据您希望用户为Wheel类提供的访问权限,只需将类的某些成员限制为非公共可见性,并将Car设为友元即可。

或者,您可以将Wheel设置为超类的protected嵌套类。

这两种替代方法都会导致编译器在尝试创建没有CarWheel时抱怨成员/类型不可访问。这更接近您想传达的信息(“不要碰我的Wheel类”),而不是关于Wheel类型不完整的消息(后者可能会被解释为“您缺少正确的#include”)。此外,这样做可以避免额外的动态分配。

namespace UseFriends
{
class Car;

class Wheel
{
    friend class Car;

    Wheel() = default;
        
    Wheel(Wheel&&) = default;
    Wheel& operator=(Wheel&&) = default;

    Wheel(Wheel const&) = default;
    Wheel& operator=(Wheel const&) = default;
public:
    void rotate()
    {
        std::cout << "Wheel: *Rotates*" << std::endl;
    }
};

class Car
{
public:
    Car()
    {
    }

    void drive()
    {
        std::cout << "Vroom!" << std::endl;
        leftWheel.rotate();
    }
private:
    Wheel leftWheel;
};

} // namespace UseFriends

namespace UseNestedClass
{
class Vehicle
{
protected:
    class Wheel
    {
    public:
        void rotate()
        {
            std::cout << "Wheel: *Rotates*" << std::endl;
        }
    };
};

class Car : private Vehicle
{
public:
    Car()
    {
    }

    void drive()
    {
        std::cout << "Vroom!" << std::endl;
        leftWheel.rotate();
    }
private:
    Vehicle::Wheel leftWheel;
};

} // namespace UseNestedClass

int main()
{
    {
        using Car = UseFriends::Car;
        using Wheel = UseFriends::Wheel;

        std::cout << "Hello World" << std::endl;
        Car myCar = Car();
        myCar.drive();

        Wheel someWheel = Wheel(); // 编译器错误:UseFriends::Wheel::Wheel() 不可访问
        someWheel.rotate();
    }

    {
        using Car = UseNestedClass::Car;
        using Wheel = UseNestedClass::Vehicle::Wheel; // 编译器错误:UseNestedClass::Vehicle::Wheel 不可访问

        std::cout << "Hello World" << std::endl;
        Car myCar = Car();
        myCar.drive();
    }
}

请注意,我只提供了代码部分的翻译,没有回答其他问题。

英文:

Depending on the access you want the user to give to the Wheel class, simply restricting some members of the class to non-public visibility and making Car a friend can do the trick.

Alternatively you could make Wheel a protected nested class of a supertype.

Both of these alternatives result in a compiler complaining about the inaccessibility of members/types when trying to create a Wheel without a Car. This is closer to the message you're trying to convey ("Keep your hands of my Wheel class.") than a message about the type of Wheel being incomplete. (The latter is likely to be interpreted as "You're missing the correct #include".) Furthermore it avoids an extra dynamic allocation.

namespace UseFriends
{
class Car;
class Wheel
{
friend class Car;
Wheel() = default;
Wheel(Wheel&amp;&amp;) = default;
Wheel&amp; operator=(Wheel&amp;&amp;) = default;
Wheel(Wheel const&amp;) = default;
Wheel&amp; operator=(Wheel const&amp;) = default;
public:
void rotate()
{
std::cout &lt;&lt; &quot;Wheel: *Rotates*&quot; &lt;&lt; std::endl;
}
};
class Car
{
public:
Car()
{
}
void drive()
{
std::cout &lt;&lt; &quot;Vroom!&quot; &lt;&lt; std::endl;
leftWheel.rotate();
}
private:
Wheel leftWheel;
};
} // namespace UseFriends
namespace UseNestedClass
{
class Vehicle
{
protected:
class Wheel
{
public:
void rotate()
{
std::cout &lt;&lt; &quot;Wheel: *Rotates*&quot; &lt;&lt; std::endl;
}
};
};
class Car : private Vehicle
{
public:
Car()
{
}
void drive()
{
std::cout &lt;&lt; &quot;Vroom!&quot; &lt;&lt; std::endl;
leftWheel.rotate();
}
private:
Vehicle::Wheel leftWheel;
};
} // namespace UseNestedClass
int main()
{
{
using Car = UseFriends::Car;
using Wheel = UseFriends::Wheel;
std::cout &lt;&lt; &quot;Hello World&quot; &lt;&lt; std::endl;
Car myCar = Car();
myCar.drive();
Wheel someWheel = Wheel(); // compiler error: UseFriends::Wheel::Wheel() is inaccessible
someWheel.rotate();
}
{
using Car = UseNestedClass::Car;
using Wheel = UseNestedClass::Vehicle::Wheel; // compiler error: UseNestedClass::Vehicle::Wheel is inaccessible
std::cout &lt;&lt; &quot;Hello World&quot; &lt;&lt; std::endl;
Car myCar = Car();
myCar.drive();
}
}

huangapple
  • 本文由 发表于 2023年6月8日 17:51:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76430629.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定