动态数组模板 Class:ostream& 运算符友元函数有问题

Dynamic Array Template Class: problem with ostream& operator friend function

各位计算机科学家大家好, 我的代码有很多问题,除了 friend ostream& 运算符函数外,一切正常。将我的 class 对象发送到 cout 时,我不断收到编译器错误。我在想在声明 friend 函数时出错,或者在它的 declaration.Here 中可能是代码:

顺便说一句,我知道使用 T 表示模板是传统做法,但我使用了我教授的名字,这听起来很奇怪,但在我的代码中使用非常规名称有助于我记住模板等编程概念

#include <iostream>
#include <cstdlib>

using namespace std;

template <class Chris>
class DynamicArray{


private:
    Chris *myArray;
     int capacity;
     int num_items;


public:
  DynamicArray();
  DynamicArray(int initialCapacity);
  void reSize(int newCapacity);
  void addElement(const Chris element);
  Chris& operator[](int index)const;
  friend std::ostream& operator << (std::ostream& outputStream, const 
  DynamicArray<Chris>& obj);
  virtual ~DynamicArray();


};


int main(){



DynamicArray<int> Array(20);

Array.addElement(20);
Array.addElement(12);
Array.addElement(13);
Array.addElement(45);
Array.addElement(78);



cout<<Array<<endl;












return 0;
}



template<class Chris>
ostream& operator<< (ostream& outputStream, const DynamicArray<Chris>& 
obj)
{

for(int index=0; index<obj.num_items; index++){

    if(index<(obj.num_items-1)){
    outputStream<<obj.myArray[index]<<",";
    }

    else{

        outputStream<<obj.myArray[index];
    }
   }


   return outputStream;
  }

 template<class Chris>
 DynamicArray<Chris>::DynamicArray():capacity(1),num_items(0)
 {

     myArray=new Chris[capacity];
 }

template <class Chris>
DynamicArray<Chris>::DynamicArray(int initialCapacity):num_items(0)
{

       if(initialCapacity>0){

    capacity=initialCapacity;
    myArray=new Chris[capacity];
}

    else{

       cout<<"ERROR, capacity cannot be negative or 0 " <<endl;
       exit(0);

      }

   }

 template <class Chris>
void DynamicArray<Chris>::reSize(int newCapacity)
 {

    if(newCapacity<=capacity){


        cout<<"ERROR, the new capacity must be greater than the current 
      capacity"<<endl;
        exit(1);
    }

    Chris *biggerArray = new Chris[newCapacity];

    for(int index=0; index<num_items; index++){

        biggerArray[index]=myArray[index];



    }

    delete [] myArray;
    capacity=newCapacity;
    myArray= new Chris[capacity];

       for(int index=0; index<num_items; index++){

        myArray[index]= biggerArray[index];



    }

    delete [] biggerArray;


   }

template <class Chris>
Chris& DynamicArray<Chris>::operator [](int index)const
{


if(index>=num_items){


    cout<<"ERROR,ARRAYINDEX OUT OF BOUNDS " <<endl;
    exit(0);
}

return myArray[index];



}





 template<class Chris>
 void DynamicArray<Chris>::addElement(const Chris element){

    if(num_items==capacity){


        reSize(capacity*2);
    }


    myArray[num_items]=element;
    num_items++;



}



template<class Chris>
DynamicArray<Chris>::~DynamicArray()
{


  delete [] myArray;
}

编译器错误是:未定义的引用 ,它还指出我的朋友 ostream& 函数未声明为模板。 我得想办法解决这个问题

修复上述友元函数必须在程序顶部声明的问题 在 class 定义之前。包含好友函数的class也应该是declared.I编辑了我的答案,也包含了整个程序。

#include <iostream>
#include <cstdlib>

using std::iostream;
using std::cout;
using std::endl;


template<typename Chris>
class DynamicArray; //must add this

template<typename Chris>
std::ostream& operator <<(std::ostream& outputStream, const 
DynamicArray<Chris>& 
    obj);// must add this as well



template <typename Chris>
 class DynamicArray{


    private:
    Chris *myArray;
    int capacity;
     int num_items;
     friend std::ostream& operator << <>(std::ostream& outputStream, const 
     DynamicArray& 
      obj);


    public:
    DynamicArray();
    DynamicArray(int initialCapacity);
    void reSize(int newCapacity);
    void addElement(const Chris element);
    Chris& operator[](int index)const;
    virtual ~DynamicArray();





















  };



  int main(){



   DynamicArray<int> Array(20);

   Array.addElement(20);
   Array.addElement(12);
   Array.addElement(13);
   Array.addElement(45);
   Array.addElement(78);



   cout<<Array<<endl;

    return 0;
   }

 template<typename Chris>
 DynamicArray<Chris>::DynamicArray():capacity(1),num_items(0)
   {

       myArray=new Chris[capacity];
    }

template <typename Chris>
DynamicArray<Chris>::DynamicArray(int initialCapacity):num_items(0)
    {

          if(initialCapacity>0){

           capacity=initialCapacity;
            myArray=new Chris[capacity];
      }

      else{

             cout<<"ERROR, capacity cannot be negative or 0 " <<endl;
              exit(0);

             }

            }

 template <typename Chris>
 void DynamicArray<Chris>::reSize(int newCapacity)
         {

                 if(newCapacity<=capacity){


                  cout<<"ERROR, the new capacity must be greater than the 
                 current capacity"<<endl;
                  exit(1);
                 }

                Chris *biggerArray = new Chris[newCapacity];

               for(int index=0; index<num_items; index++){

               biggerArray[index]=myArray[index];



              }

              delete [] myArray;
              capacity=newCapacity;
              myArray= new Chris[capacity];

               for(int index=0; index<num_items; index++){

               myArray[index]= biggerArray[index];



              }

              delete [] biggerArray;


             }

 template <typename Chris>
 Chris& DynamicArray<Chris>::operator [](int index)const
 {


    if(index>=num_items){


      cout<<"ERROR,ARRAYINDEX OUT OF BOUNDS " <<endl;
       exit(0);
   }

     return myArray[index];



   }



template<typename Chris>
void DynamicArray<Chris>::addElement(const Chris element){

    if(num_items==capacity){


        reSize(capacity*2);
    }


    myArray[num_items]=element;
    num_items++;



}

template<typename Chris>
std::ostream& operator<< (std::ostream& outputStream, const 
DynamicArray<Chris>&
obj)
{

   for(int index=0; index<obj.num_items; index++){

   if(index<(obj.num_items-1)){
    outputStream<<obj.myArray[index]<<",";
 }

     else{

         outputStream<<obj.myArray[index];
    }
   }


      return outputStream;
  }

template<typename Chris>
DynamicArray<Chris>::~DynamicArray()
{


   delete [] myArray;
}

虽然OP好像自己解决了her/his问题,但我还是有点好奇。

OP 的问题似乎是在 class 模板中声明一个独立的 friend operator<<。 OP的示例代码有点难读,所以我自己写了MCVE:

#include <iostream>
#include <exception>
#include <algorithm>

// template class for dynamic array
template <typename VALUE>
class VectorT {
    
  private:
    VALUE *_values;
    size_t _capacity;
    size_t _size;
    
  public:
    VectorT(): _values(nullptr), _capacity(0), _size(0) { }
    ~VectorT() { delete[] _values; }
    VectorT(const VectorT &vec); /// @todo
    VectorT& operator=(const VectorT &vec); /// @todo
    
    size_t capacity() const { return _capacity; }
    size_t size() const { return _size; }
    VALUE& operator[](size_t i) { return _values[i]; }
    const VALUE& operator[](size_t i) const { return _values[i]; }
    
    void push_back(const VALUE &value)
    {
      if (_size == _capacity) { // realloc necessary
        const size_t capacity = std::max(2 * _capacity, (size_t)1);
        VALUE *const values = new VALUE[capacity];
        if (!values) throw std::bad_array_new_length();
        std::move(_values, _values + _size, values);
        delete[] _values;
        _values = values; _capacity = capacity;
      }
      _values[_size++] = value;
    }

    friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
    
};

// output stream operator for VectorT
template <typename VALUE>
std::ostream& operator<<(std::ostream &out, const VectorT<VALUE> &vec)
{
  const char *sep = "";
  for (size_t i = 0; i < vec._size; ++i) {
    out << sep << vec[i];
    sep = ", ";
  }
  return out;
}

// test
int main()
{
  VectorT<int> vec;
  // populate vec
  vec.push_back(20);
  vec.push_back(12);
  vec.push_back(13);
  vec.push_back(45);
  vec.push_back(78);
  // test output operator
  std::cout << vec << '\n';
  // done
  return 0;
}

注意:我稍微更改了概念和名称,因为 OP 的 DynamicArray 实际上提供了类似于 std::vector 的内容。我觉得再接近一点是合理的。

我试着用

编译这个
g++ --version ; g++ -std=c++11 -O2 -Wall -pedantic main.cpp && ./a.out

并得到以下输出:

g++ (GCC) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

main.cpp:38:73: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&)' declares a non-template function [-Wnon-template-friend]
     friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
                                                                         ^
main.cpp:38:73: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 
/tmp/ccvsl6kw.o: In function `main':
main.cpp:(.text.startup+0x9e): undefined reference to `operator<<(std::ostream&, VectorT<int> const&)'
collect2: error: ld returned 1 exit status

这很有趣

  1. ful 答案已由 g++
  2. 给出
  3. 它实际上包含两个活动:

make sure the function template has already been declared

and

add <> after the function name here

关于第一部分,我想起了我曾经在的回答中发现的类似问题。

第二部分(在此处的函数名称后添加 <>)引起了我的注意,因为我以前从未见过(也没有使用过)这种方式。所以,我想详细说明一下。

插入以下前向声明后:

// forward declaration of VectorT
template <typename VALUE>
class VectorT;

// prototyping of output stream operator
template <typename VALUE>
std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);

我尝试再次编译,再次得到:

main.cpp:46:73: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&)' declares a non-template function [-Wnon-template-friend]
     friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
                                                                         ^
main.cpp:46:73: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 
/tmp/ccXLnkbV.o: In function `main':
main.cpp:(.text.startup+0x9e): undefined reference to `operator<<(std::ostream&, VectorT<int> const&)'
collect2: error: ld returned 1 exit status

和以前一样。糟糕!

我的第一反应是将 friend operator 更改为 template friend operator:

template <typename VALUE_>
friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE_>&);

这解决了问题:Live Demo on coliru.


但是,此解决方案有一个小缺陷,可能会或可能不会令人讨厌:任何运算符实例都是任何 VectorT 模板实例的友元。实际上,这应该仅限于一个运算符实例——签名中具有相同 VectorT 模板实例的那个。这就是 g++ 实际建议的内容:

friend std::ostream& operator<< <>(std::ostream&, const VectorT<VALUE>&);

Live Demo on coliru

我想知道为什么 中没有提到这一点 – 恕我直言,这是 OP 修复中真正令人兴奋的部分。


最后,我想提一下(在这种情况下)“整个 friend 魔术”是完全没有必要的。这是首先引起我注意的——在几十个书面输出操作符(对于 class 模板)中,我从来没有遇到任何 friend 问题。如果输出运算符仅使用 classpublic const 成员(我很难想象为什么它们不可用),这可以很容易地避免:

// output stream operator for VectorT
template <typename VALUE>
std::ostream& operator<<(std::ostream &out, const VectorT<VALUE> &vec)
{
  const char *sep = "";
  for (size_t i = 0; i < vec.size(); ++i) {
    out << sep << vec[i];
    sep = ", ";
  }
  return out;
}

(删除了前向声明和 friend 运算符,因为不再需要了。)

Live Demo on coliru