لماذا لا يمكن استخدام الإعلان الأمامي لـ std::vector؟

StackOverflow https://stackoverflow.com/questions/37346

  •  09-06-2019
  •  | 
  •  

سؤال

إذا قمت بإنشاء فصل مثل ذلك:

// B.h
#ifndef _B_H_
#define _B_H_

class B
{
private:
    int x;
    int y;
};

#endif // _B_H_

واستخدامها مثل هذا:

// main.cpp
#include <iostream>
#include <vector>

class B; // Forward declaration.

class A
{
public:
    A() {
        std::cout << v.size() << std::endl;
    }

private:
    std::vector<B> v;
};

int main()
{
    A a;
}

فشل المترجم عند الترجمة main.cpp.الآن الحل الذي أعرفه هو #include "B.h", ، لكني أشعر بالفضول لمعرفة سبب فشلها.لا g++ أو clوكانت رسائل الخطأ مفيدة للغاية في هذا الشأن.

هل كانت مفيدة؟

المحلول

يحتاج المترجم إلى معرفة حجم "B" قبل أن يتمكن من إنشاء معلومات التخطيط المناسبة.إذا قلت بدلا من ذلك std::vector<B*>, ، فلن يحتاج المترجم إلى معرفة حجم B لأنه يعرف حجم المؤشر.

نصائح أخرى

في الواقع، سيتم إنشاء المثال الخاص بك إذا تم تنفيذ مُنشئ A في وحدة ترجمة تعرف نوع B.

مثيل std::vector له حجم ثابت، بغض النظر عن T، لأنه يحتوي، كما قال آخرون من قبل، فقط على مؤشر إلى T.لكن منشئ المتجه يعتمد على نوع الخرسانة.لم يتم تجميع المثال الخاص بك لأن A() يحاول استدعاء مُنشئ المتجه، والذي لا يمكن إنشاؤه دون معرفة B.إليك ما قد ينجح:

تصريح أ:

// A.h
#include <vector>

class B; // Forward declaration.

class A
{
public:
    A(); // only declare, don't implement here

private:
    std::vector<B> v;
};

التنفيذ أ:

// A.cpp
#include "A.h"
#include "B.h"

A::A() // this implicitly calls vector<B>'s constructor
{
    std::cout << v.size() << std::endl;
}

الآن يحتاج مستخدم A إلى معرفة A فقط، وليس B:

// main.cpp
#include "A.h"

int main()
{
    A a; // compiles OK
}

لإنشاء مثيل A::v، يحتاج المترجم إلى معرفة النوع المحدد لـ B.

إذا كنت تحاول تقليل كمية الأمتعة #المضمنة لتحسين أوقات التجميع، فهناك شيئان يمكنك القيام بهما، وهما في الحقيقة اختلافات عن بعضهما البعض:

  1. استخدم المؤشر إلى B
  2. استخدم خفيف الوزن الوكيل إلى ب

إنه أكثر من مجرد حجم B المطلوب.سيكون لدى المترجمين الحديثين حيل رائعة لتسريع النسخ المتجهة باستخدام memcpy حيثما أمكن ذلك، على سبيل المثال.يتم تحقيق ذلك عادةً من خلال التخصص جزئيًا في POD لنوع العنصر.لا يمكنك معرفة ما إذا كان B هو POD من الإقرار الأمامي.

تمامًا كما قال fyzix، السبب وراء عدم عمل الإعلان الأمامي الخاص بك هو المُنشئ المضمّن الخاص بك.حتى المنشئ الفارغ قد يحتوي على الكثير من التعليمات البرمجية، مثل إنشاء أعضاء غير POD.في حالتك، لديك متجه لتهيئته، وهو ما لا يمكنك فعله دون تحديد نوع القالب الخاص به بشكل كامل.

الشيء نفسه ينطبق على المدمرين.يحتاج المتجه إلى تعريف نوع القالب لمعرفة المدمر الذي يجب الاتصال به عند تدمير المثيلات التي يحتفظ بها.

للتخلص من هذه المشكلة، فقط لا تقم بتضمين المنشئين والمدمرين.قم بتعريفها بشكل منفصل في مكان ما بعد تعريف B بالكامل.

للمزيد من المعلومات،http://www.chromium.org/developers/coding-style/cpp-dos-and-donts

لا يهم ما إذا كنت تستخدم ناقلًا أو تحاول فقط إنشاء مثيل B.يتطلب إنشاء مثيل التعريف الكامل للكائن.

يا رجل، أنت تقوم بالمثل std::vector مع نوع غير مكتمل.لا تلمس الإعلان الأمامي، فقط انقل تعريف المنشئ إلى ملف .cpp ملف.

سبب عدم قدرتك على استخدام التصريح الأمامي هو أن حجم B غير معروف.

لا يوجد سبب في مثالك يمنعك من تضمين B.h داخل A.h، فما هي المشكلة التي تحاول حلها حقًا؟

يحرر: هناك طريقة أخرى لحل هذه المشكلة أيضًا:التوقف عن استخدام C/C++!إنها السبعينيات...;)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top