正如标题所示,我在C ++中有一个OOP错误,LNK2001未解析的外部错误。这是我的代码,我哪里出错了? 我使用sfml来使用VS2015中的图形。
// OOPExample.hpp
#pragma once
#include <SFML\Graphics.hpp>
#include <SFML\System.hpp>
#ifndef OOPEX_H
#define OOPEX_H
class OOPExample{
public:
static sf::CircleShape shape;
static float rot;
static void draw();
static void rotate();
};
#endif // OOPEX_H
// OOPExample.cpp
#include "OOPExample.hpp"
#include <SFML\Graphics.hpp>
void OOPExample::rotate() {
OOPExample::rot += 0.1f;
return;
};
void OOPExample::draw() {
OOPExample::shape.setFillColor(sf::Color::Red);
OOPExample::shape.setRotation(rot);
return;
};
// Source.cpp
#include <SFML/Graphics.hpp>
#include "OOPExample.hpp"
int main()
{
sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
OOPExample oopexample();
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(oopexample().shape);
window.display();
}
return 0;
}
从我所看到的情况来看,我需要定义OOPExample.hpp中声明的方法,但我在OOPExample.cpp中有那些确切的定义。我在Source.cpp中错误地实例化了类吗?
答案 0 :(得分:1)
关于链接错误的问题。评论突出了大部分内容......这些不是OOP错误,而是构建项目时的链接时错误。我不知道你是否有编译系统级语言的经验;但是学习编译链接循环的基础知识以及链接器在将最终程序放在一起时的期望是一个好主意。下面是一个如何定义静态成员变量的简单示例。
// class1.h
Class1
{
public:
private:
static float rotation;
};
// class1.cpp
#include "class1.h"
int Class1::rotation = 5.0f;
请注意int Class1::rotation = 5.0f;
在程序初始化时发生一次。
我不知道你是否正在按照他们正在创建这个类的教程,但你有一些令人担忧的静态成员。这是一个OOP问题。如果要创建OOPExample的许多对象/实例,则需要了解静态意味着什么。在类的上下文中,当您将static关键字应用于变量时,意味着所有OOPExample对象将共享该一个变量。这使得静态成员变量适用于默认值以及类似给定类的数量。您可以使用static int OOPExample::count;
来计算您所创建的OOPExample对象的数量。我稍后会把它放在一个例子中。
链接错误可能有很多原因,尤其是缺少定义。 PcAF在对您的问题的评论中突出了一个重要的内容。但是您可能也缺少SFML库。我依稀记得SFML教程,包括如何在您的环境中链接其库的详细说明。在VS中,它将位于项目属性中的某个位置。如果你在标题中声明了一些不在实现中的东西(通常是cpp),你显然会得到类似的错误。这是静态变量的情况,但也适用于函数。
现在你提供的三个文件有很多错误。我编辑它们以突出一些问题,但它远非完美。我不会这样做,因为sf :: CircleShape已经是一个面向对象的实体。它具有您尝试实施的所有已实施的内容。从来没有过度抽象一个问题(我在某个时候也意识到我们正在旋转一个圆圈哈哈)。你应该真的按照建议来获得一本好的教科书,并从头开始。 SFML是一个庞大的库,会让您分心,无法理解C ++的基础知识。 OOP只是C ++的一个方面,你需要拥抱所有C ++基础知识才能有效地使用OOP。如果你这样做,你将拥有最强大的抽象机制(在我看来)。
我的编辑如下,但实际上,这只是兔子洞深度的证明(它变得更糟)。如何实例化OOPExample显示在main。
中// OOPExample.h
#ifndef OOPEX_H
#define OOPEX_H
// Only include what you need to. Users of this header should be exposed to as
// little SFML as possible.
#include <SFML/Graphics/CircleShape.hpp>
class OOPExample{
public:
// Parameterless constructor.
OOPExample(); // Note, this sets the rotation to the default rotation.
// One that takes a initial rotation.
OOPExample(float initial_rotation);
// Rotate 0.1f (by default) or by user specified amount.
void rotate(float rotation = 0.1f);
// window.draw() takes a Drawable as its first argument. Fortunately,
// CircleShape is a shape which in turn is a Drawable. Notice that we
// return by constant reference. Callers cannot edit our shape but they
// get a reference to the sf::CircleShape shape instance so they can read
// it.
// const, & (i.e. reference), pointers requires a deep understanding of
// object ownership, copying by value, by reference, and now of
// particular interest in C++11, moving.
const sf::CircleShape &getShape() const;
// You forgot to declare and define this.
void setRotation(float rotation);
// Set the default rotation for all objects created with the
// parameterless constructor.
static void setDefaultRotation(float rotation);
// The destructor.
virtual ~OOPExample();
private:
sf::CircleShape shape;
// sf::CircleShape already has a rotation with getters and setters.
// Don't over abstract!
// Our setRotation, rotate functions seem a bit unneccesary.
// float rotation;
// Defaults.
static sf::CircleShape default_circle;
static float default_rotation;
// Count example.
static int count;
};
#endif // OOPEX_H
// OOPExample.cpp
// Personally, and I know with most other C++ developers, I prefer my header's
// extension to be .h. .hpp usually identifies headers with
// implementations/definitions of classes in the header file itself (necessary
// in some circumstances).
#include "OOPExample.h"
//
// How to initialise static member variables. This happens once at the
// beginning of the program.
//
// All our circles have a default radius of 5.
sf::CircleShape OOPExample::default_circle = sf::CircleShape(5);
// And rotation of 0.
float OOPExample::default_rotation = 0;
int OOPExample::count = 0;
// The definition of the parameterless constructor.
OOPExample::OOPExample()
// A class initialiser list. How we build a new object.
: shape(default_circle) // We copy the default circle.
{
// Do other stuff to construct the object if you need to. For example:
shape.setFillColor(sf::Color::Red);
setRotation(default_rotation);
count++; // We just made another OOPEXample instance.
}
// The definition of a constructor that takes an initial rotation. I just
// realised we are rotating a circle!
OOPExample::OOPExample(float initial_rotation)
: shape(default_circle) // We copy the default circle.
{
// Do other stuff to construct the object if you need to. For example:
shape.setFillColor(sf::Color::Red);
// Notice: we used the user provided argument this time.
setRotation(initial_rotation);
count++; // We just made another OOPEXample instance.
}
void OOPExample::rotate(float rotation)
{
shape.rotate(rotation);
// return; // No need to specify a return for a void.
}
const sf::CircleShape &OOPExample::getShape() const
{
return shape;
}
void OOPExample::setRotation(float rotation)
{
shape.setRotation(rotation);
}
void OOPExample::setDefaultRotation(float rotation)
{
// OOPExample scoping is unnecessary.
OOPExample::default_rotation = rotation;
}
OOPExample::~OOPExample()
{
// Do things required for cleanup, i.e. deinit.
// One OOPExample just reached the end of its lifetime. Either it
// was deleted or reached the end of the
// scope (i.e. {}) it was created in.
count--;
}
// main.cpp
#include "OOPExample.h"
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
// Call the default, parameterless, constructor to instantiate an object
// of OOPExample.
OOPExample oopexample;
// Create another with a initial rotation of your choosing.
OOPExample another_obj(0.5f);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
// The instance object of OOPexample is referred to by oopexample.
// window.draw(oopexample().shape);
// This member is now private.
//window.draw(oopexample.shape);
window.draw(oopexample.getShape());
window.display();
}
return 0;
}