英文:
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?
- If so, can we achieve this without the use of a pointer
-
我们是否应该完全隐藏“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 <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.
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?
- If so, can we achieve this without the use of a pointer
答案1
得分: 2
根据您希望用户为Wheel
类提供的访问权限,只需将类的某些成员限制为非公共可见性,并将Car
设为友元即可。
或者,您可以将Wheel
设置为超类的protected
嵌套类。
这两种替代方法都会导致编译器在尝试创建没有Car
的Wheel
时抱怨成员/类型不可访问。这更接近您想传达的信息(“不要碰我的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&&) = 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(); // 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 << "Hello World" << std::endl;
Car myCar = Car();
myCar.drive();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论