我相对较新的C ++(之前的Python经验和涉猎Java)我正在编写一个小程序作为熟悉项目。作为程序的一部分,我正在编写一个类来解码一些数据,并最终编写一个类似的将执行编码的类。代码是我确信我会经常重用的东西,并认为创建一个库作为项目的一部分会很有趣。 我的问题是,什么被认为是创建图书馆的最佳做法?
编辑:(已修订)
在问了这个问题后,我意识到我不知道我不知道的是什么。我做了一些更多的研究,这应该有助于使我的问题更具体:
原始问题:(针对具体情况)
我最初的想法是创建包含MyEncoder和MyDecoder类的MyLib是最方便的。
如果我这样做,我只是在标题中声明这两个类吗?
我想从这个库中创建一个DLL,以实现可移植性和体验。我确信有很多关于创建和使用DLL的信息(这不是这个问题的主题),但是如果有一个特别好的教程(对于Qt)请传递它。
我的假设是,对于此实现而言,最好为MyEncoder和MyDecoder使用单独的命名空间,而不是MyLib的一个命名空间?
我可以看到这种方法的一个折衷是应用程序的大小,因为包括MyLib.h将包括编码器和解码器的代码(如果编码器和解码器是单独的应用程序)。这假设我没有使用DLL。
我想我得到的是:
如果它更具体,我在Qt Creator中使用Qt 4.7.4进行开发。
答案 0 :(得分:6)
关于库,C ++中的一个“最佳实践”通常是“你付出的代价”。
这适用于您的问题是您将MyEncoder和MyDecoder放在单独的头文件中。因此,如果用户想要使用MyEncoder,他将包含MyEncoder.h,如果他想使用MyDecoder,他将包含MyDecoder.h,如果他想使用两者,他将包括两个头。
链接器通常只包含您在可执行文件中使用的代码部分,因此,就代码大小而言,没有任何代价,但编译时间会受到惩罚,特别是如果您开始使用高级模板技术在你的课堂上。在大型项目中编译时间可能会很长,因此只能包含您要使用的内容非常重要。
当然,有时候用一个标题包含所有东西也很方便。所以你可以拥有的是:
然后MyCodec.h可以包含MyEncoder.h和MyDecoder.h
将MyEncoder和MyDecoder放在不同的命名空间中可能没有充分的理由,假设它们是针对相同类型的数据进行操作的。
您可能希望拥有类似MyCodec名称空间的内容,并在该名称空间中声明MyEncoder和MyDecoder。
针对您的修订进行了更新:
作为一个库,我的假设是,添加MyEncoder,我只是创建 另一个类/头文件。
这是一个正确的假设。
接下来发生的事情是我不确定的地方。我只是建立了 图书馆?我的理解是它将创建一个.lib(在Windows中) 和.h文件。在这一步之前我应该做些什么吗?是 有哪些选项会影响我与它互动的方式?
我暂时没有使用过Qt创建者,所以我不能对它有权威或者如何访问相关选项。但作为一般规则,您将需要至少有两个版本的库;调试版本和发布版本。如果您的库使用Qt库,那么当应用程序链接到库的调试版本时,他们需要在其路径中具有Qt共享库的调试版本,如果它们链接到您的发行版本,则需要拥有Qt库的发布版本。
您可能还有一些选项可以静态链接到C ++标准运行时库,还是动态链接到DLL。
但基本上是的,您只需构建库,然后使用它的应用程序将库链接到可执行文件。
我是否只在程序中包含该头文件以访问它们 我写的课程?
您包含头文件,并链接到.lib文件。这就是你应该做的所有事情。
答案 1 :(得分:4)
这适用于Qt Creator 2.4.1。
首先,要使用和调试您的库,您将希望拥有一个包含两个子项目的分层顶级项目:一个用于库,另一个用于使用它的应用程序。这样,Creator将重建在运行应用程序之前更改的所有内容 - 因此,如果您更改库源文件,库将自动重建以及应用程序将重新链接。
我将逐步展示如何创建所需的三个项目(库,应用程序和顶级项目),最后是一个可构建,可调试,最小的应用程序和库。您可以按照相同的步骤轻松添加更多子库。
为您的项目创建一个文件夹。
热门项目
File->New File or Project, Other Project, Subdirs Project, Choose
top
,并将其放在您在步骤1中创建的文件夹中。图书馆计划 - 在上一步中点击后会显示新项目窗口。
Other Project, C++ Library, Choose
library
,并将其放在先前创建的top
文件夹中
步。默认情况下,您应该已经选择了此文件夹。应用程序项目 - 右键单击“项目”窗格中的top
项目(不在top.pro上)
Qt Widget Project, Qt Gui Application, Choose
app
并将其放入在步骤2中创建的top
文件夹中。默认情况下,您已经选择了此文件夹。您现在应该有一个可构建的top
项目,其中app
和library
作为子项目。按C-B(Ctrl-B或Command-B,具体取决于您的平台)来构建它。构建应该没有错误。
app
子项目尚未使用我们的库。要使用我们的库app
链接:
右键单击“项目”窗格中的app
(app.pro上不),选择Add Library...
选择Internal Library
,继续
library
是唯一的选择,已经被选中。点击直到结束。 app
项目现在静态链接到library
。更改app
项目中的任何文件(空格更改将执行)以强制构建,然后按C-B进行构建。构建应该通过OK。
如果库已更改,将重建app
子项目,但在开始构建应用程序之前,没有任何东西可以保证make系统首先构建库子项目。此类信息属于顶级top.pro
项目文件。
打开top.pro
,然后添加一行CONFIG += ordered
。该文件应如下所示:
TEMPLATE = subdirs
SUBDIRS += \
library \
app
CONFIG += ordered
确保SUBDIRS的订购正确:库在应用之前首先出现。如果订单错误,您可以将条目按顺序排列。反斜杠\
是一个行继续符。 \
之后必须没有空格!
保存文件(C-S),右键单击top
项目并选择Rebuild Project" top"。构建应该没有错误地完成。警告没问题。
在Qt Creator的左下方面板中,有一个面板,其中包含构建按钮(锤子),运行按钮(三角形),调试按钮(带有错误的三角形),构建选择器(a上面有top
项目名称的笔记本电脑,以及下面的elided build配置。单击构建选择器,并确保选择以Debug
结尾的Build。此构建配置为生成所需的调试符号在调试器下运行它。我们稍后会需要它。
我们现在将为Qt Creator为我们创建的Library类添加一些代码。打开library.h
和library.cpp
文件。添加的代码只是Library类中的静态方法。这个方法将在App中用来表明这两个部分实际上已被链接。这两个文件的内容如下。
//library.h
#ifndef LIBRARY_H
#define LIBRARY_H
#include <QString>
class Library {
public:
Library();
static QString string();
};
#endif // LIBRARY_H
//library.cpp
#include "library.h"
Library::Library()
{}
QString Library::string()
{
return "I come from the library";
}
点击C-B,代码应该重建而不会出错。
现在让我们使用app
中的库API。打开mainwindow.cpp
和mainwindow.h
文件并修改如下。
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QScopedPointer>
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QScopedPointer<Ui::MainWindow> const ui;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include <QVBoxLayout>
#include <QLabel>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "library.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QVBoxLayout * layout = new QVBoxLayout();
QLabel * label = new QLabel(Library::string(), centralWidget());
layout->addWidget(label);
centralWidget()->setLayout(layout);
}
MainWindow::~MainWindow()
{}
点击C-B,项目应该建立好。
然后我们可以在Library::string()
实现中设置断点,以便不仅调用代码,而且调试器跨子项目工作。在library.cpp
中,点击return "I come from the library";
行中行号左侧的灰色区域。在您单击的位置会出现带有小沙漏的红色圆圈:这表示已设置挂起断点。
接下来按C-Y在调试器中启动项目。唯一可运行的子项目是应用程序,它将自动被选为启动项目。
最终断点圈会丢失沙漏,因为调试器会读取符号文件并找出断点的位置。此后不久,代码将在Library :: string()方法中停止 - 它从MainWindow构造函数中调用。点击C-Y继续执行app
。
现在将出现主窗口,其中可以看到I come from the library
文本。此文本设置在MainWindow的构造函数中添加的标签上。
答案 2 :(得分:2)
库和类之间没有链接。库的概念早在OO之前出现,并且c / c ++中的库格式中没有任何内容与类或名称空间有关。
如果您正在使用静态库(而不是DLL),那么链接器将仅提取程序所需的那些函数,因此库的大小无关紧要。使用动态库(dll)你必须运送整个dll,所以有理由将类似的lib打包成一个dll - 这就是为什么Qt有例如QtOpenGl.dll与QtGui.dll分开 - 如果你不使用opengl不需要包含库。头文件的大小与完成的程序无关 - 尽管非常大的头文件会导致编译速度变慢(除非你有预编译的头文件)。
命名空间用于表示这些类一起工作并避免与其他类冲突,因此我将使用单个命名空间来编码/解码类。