0

我似乎无法确定这个问题,所以我想我会在这里问。我知道为什么通常会出现此错误,但这次我似乎无法弄清楚。可能是一些我不知道或遗漏的简单事情。

g++  -c -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT src/Render.cpp -L/usr/lib/i386-linux-gnu -lSDL -lSDL_image
src/Render.cpp:8:1: error: ‘Render’ does not name a type
src/Render.cpp:13:1: error: ‘Render’ does not name a type
src/Render.cpp:18:14: error: ‘Render’ has not been declared
src/Render.cpp:31:6: error: ‘Render’ has not been declared
make: *** [Render.o] Error 1

这是我的 core.cpp、Makefile 和 Render.h

渲染.cpp

#ifndef _Render_h
#define _Render_h
#include <iostream>
#include "SDL/SDL_image.h"

using namespace std;

Render::Render()
{

}

Render::~Render()
{

}

SDL_Surface* Render::loadImg(string filename)
{
    SDL_Surface* temp = NULL;
    SDL_Surface* optimized = NULL;

    if((temp = IMG_Load(filename.c_str())) != NULL)
    {
        optimized = SDL_DisplayFormat(temp);
        SDL_FreeSurface(temp);
    }
    return optimized;
}

void Render::applySurface(int x, int y, SDL_Surface* source,
        SDL_Surface* destination)
{
    SDL_Rect offset;
    offset.x = x;
    offset.y = y;
    SDL_BlitSurface(source, NULL, destination, &offset);    
}

#endif

核心.cpp

#include <iostream>
#include "SDL/SDL.h"
#include "Render.h"

using namespace std;

int main(int argc, char* args[])
{
    string imgGoku = "src/imgs/Goku.bmp";
    string imgVegeta = "src/imgs/Vegeta.png";
    const int SCREEN_WIDTH = 640;
    const int SCREEN_HEIGHT = 480;
    const int SCREEN_BPP = 32;
    SDL_Surface *message = NULL;
    SDL_Surface *background = NULL;
    SDL_Surface *screen = NULL;
    Render rnd;

    if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
    {
        return 1;
    }

    screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT,
            SCREEN_BPP, SDL_SWSURFACE);
    if(screen == NULL)
    {
        return 1;
    }

    SDL_WM_SetCaption("Hello World", NULL);
    rnd.applySurface(10,10,rnd.loadImg(imgGoku),screen);
    if(SDL_Flip(screen) == -1)
    {
        return 1;
    }

    SDL_Delay(2000);
    SDL_FreeSurface(message);
    SDL_FreeSurface(background);
    SDL_Quit();
    return 0;
}

渲染.h

#include "SDL/SDL.h"

using namespace std;

class Render{
    public:
        Render();
        ~Render();
        SDL_Surface* loadImg(string filename);
        void applySurface(int x, int y, 
                SDL_Surface* source, 
                SDL_Surface* destination);
};

生成文件

#Game Make file
TARGET = game.exe
OBJS = core.o \
       Render.o \

SDL_CFLAGS := $(shell sdl-config --cflags)
SDL_LDFLAGS := $(shell sdl-config --libs) -lSDL_image
CFLAGS = -Wall
LIBS =
LDFLAGS = 

$(TARGET): $(OBJS)
       g++ $(CFLAGS) $(SDL_CFLAGS) -o $@  $(LDFLAGS) $(OBJS) $(SDL_LDFLAGS) $(LIBS)
%.o: src/%.cpp src/Render.h
       g++  -c $(SDL_CFLAGS) $< $(SDL_LDFLAGS)

.PHONY: clean
clean:
    rm -f $(TARGET) $(OBJS)
4

2 回答 2

3

您需要在 Render.cpp 的顶部包含 Render.h

于 2012-06-09T07:16:54.463 回答
3

是的。我认为这就是#ifndef 和#define 所做的,但我想我理解它们或错误地使用它们。

您误解了它们,并且错误地使用了它们。

虽然向#include "Render.h"您的 Render.cpp 文件添加指令将解决您的直接编译问题,但它并不能解决误解和误用的双重问题。您应该对代码进行另外两个修复。我将总结您应该进行的更正,然后说明您为什么应该这样做。

问题 #1:您需要将#ifndef _Render_h/#define _Render_h对和关闭#endif从源文件移动到标题。

问题 #2:从预处理器符号中删除前导下划线_Render_h

#include guards
查看几乎所有系统头文件,您会发现它们遵循非常标准的布局。该文件以描述该文件的标题注释开头。紧随其后的是一对形式的行#ifndef _SOME_NAME_H_#define _SOME_NAME_H_。文件的主体紧随其后,文件中的最后一件事是结束#endif。那#ifndef/#define对被称为#include守卫。关闭#endif总是(嗯,几乎总是;其中一些系统标题违反标准布局)在最后。

这样做是为了防止同一个标头被多次包含。假设您有头文件 foo.h、bar.h 和 baz.h。假设 foo.h 和 bar.h 都使用了 baz.h 中定义的功能。这些标题中的每一个都应该有一个#include "baz.h"指令,最好靠近顶部。现在假设你的 main.cpp#include是 foo.h 和 bar.h。如果文件 baz.h 没有#include保护,它将在 main.cpp 中包含两次。您的 main.cpp 很可能无法编译。

这个多重包含问题的解决方案是#include在文件 baz.h 中放置一个守卫。现在#include "foo.h"main.cpp 中的将 baz.h 的主体粘贴到您的代码中,但#include "bar.h"不会因为#includebaz.h 中的守卫将使预处理器跳过 baz.h 的主体。

#include在每个头文件中放置一个保护是个好主意。

#pragma once守卫
的概念有几个问题。#include

  • 该名称必须指定两次,一次在#ifndef指令中,另一次在#define指令中。对于墨菲定律来说,这是一个巨大的机会,可以让其丑陋的头脑抬头。程序员可以而且确实打错了这些名称中的一个或另一个。
  • 最后#endif是另一个潜在的问题来源。一些维护程序员(维护程序员是开发程序员存在的祸根)将会看到,在关闭之后#endif添加代码将快速而肮脏地解决他的棘手问题。维护程序员总是尽可能选择最快、最脏的解决方案。就这样搞定了。
  • 名称必须是唯一的。假设您只是从事大型项目的众多人中的一员,并且假设您和其他人都有一个名为 Render.h 的文件。这两个文件在两个不同的目录中,所以这两个文件同名是没有问题的。#include如果两个文件都有使用预处理器符号的保护,则会出现一个大问题Render_h

这些问题有一个很好的解决方案,#pragma once指令。这个编译指示,如果它有效的话,完全符合#include守卫的意图,但更好。只有一行而不是三行,并且没有预处理器名称。问题是“如果它有效”。这是一个编译指示,所以并不是所有的编译器都支持它,而且一些支持它的编译器也没有做对。

由于. #include_ #pragma once其他人使用#pragma once是因为可移植性问题不适用于他们的项目。还有一些人同时使用这两种机制。

前导下划线
如果您查看系统头文件,您会发现它充满了带有前导下划线的名称。这可能会给人一种印象,即使用前导下划线是正确的做法。毕竟,谁比编译器厂商更懂语言呢?

编译器供应商使用前导下划线的原因是为了避免与您的代码冲突。一个好的编译器供应商使用前导下划线正是因为如果您不在任何名称中使用前导下划线,这样做可以防止他们的名称与您的名字冲突。永远不要在类名、全局变量、自由函数或预处理器符号中使用前导下划线。前导下划线保留供编译器使用。

于 2012-06-09T10:11:04.160 回答