2016-03-02 94 views
-4

我真的在努力讓自己的指針在C++上工作。我搜索了很多問題,但我無法理解這些東西。C++指針和數組

我試圖做這個Python代碼等同於C++:

class Node: 

    def __init__(self,isBase,channelList): 
     self.isBase = isBase 
     self.channelList = channelList 

    def getChannelList(self): 
     return self.channelList 

    def isBase(self): 
     return self.isBase 

頻道列表比較麻煩的問題。它是一個整數數組。 我知道這將作爲指向我的類聲明的指針傳遞。我希望能夠將其存儲在類變量中,並能夠根據命令獲取和設置它。

的C++代碼如下:

#include "Arduino.h" 
#include "Node.h" 
#include <ESP8266WiFi.h> 
#include <WiFiUdp.h> 

int *_channelList; 
Node::Node(boolean isBase, int *channelList) 
{ 
    _isBase = isBase; 
    int i=0; 
    while(channelList[i]!='\0'){ 
     _channelList[i] = channelList[i]; 
     i++; 
    } 
    _channelList[i+1] = '\0'; 
    } 

boolean Node::isBase(){ 
    return _isBase; 
} 

int* Node::getChannelList(){ 
    return _channelList; 
} 
+0

不要將數組作爲指針傳遞。就那麼簡單。 –

+2

如果您需要固定大小的整數數組,請使用std :: array 。如果你需要動態大小,請使用std :: vector 。不要打擾指針;通過(const)引用來傳遞它們。 –

+0

也許你應該發佈你的C++代碼,以便我們可以看到你可能做錯了什麼。 –

回答

1

假設channelList是空值終止,並且_channelList應該是一個類的成員,你可以試試這個:

#include <algorithm> // For std::copy(). 

// ... 

template<size_t N> Node::Node(bool isBase, const int (&channelList)[N]) : _isBase(isBase) 
{ 
    std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList)); 
} 

除非我錯過了一些東西,應該使用任何大小的C型int數組,並將其複製到_channelList。如果傳遞的數組大於_channelList,則可能會導致問題。如果可能的話,如果尺寸是預定的,則最好使用std::array,如果不是,則使用std::vector


如果大小是固定的:

#include <array> 
#include <algorithm> // For std::copy() and std::fill(). 

const size_t _sz = [whatever]; 
// Simple way of setting size, but not the best. See below. 

class Node 
{ 
    bool _isBase; 
    std::array<int, _sz> _channelList; 

    public: 
     Node(bool isBase, const int (&channelList)[_sz]); 
     Node(bool isBase, const std::array<int, _sz>& channelList); 
     // ... 
}; 

/* Alternatively, you can code the size into the class as a magic number (not a good idea), 
* or better yet, make it a template parameter for the class: 
*  template<size_t _sz> class Node 
*  { 
*   bool _isBase; 
*   std::array<int, _sz> _channelList; 
* 
*   public: 
*    Node(/ * parameters * /); 
* 
*    template<size_t N> 
*    Node(/ * parameters * /); 
*   // ... 
*  }; 
* When using the class, you would declare an instance as "Node<SIZE>", where "SIZE" is the 
* desired size. 
* 
* Note that if you make the size a template parameter, and define the member functions 
* outside of the class' definition, you have to put the same template at the start of each 
* member function: 
*  template<size_t _sz> Node<_sz>::Node(/ * parameters * /) 
*  { 
*   // ... 
*  } 
* This also applies to templated member functions, which will have two sets of template 
* parameters. 
*  template<size_t _sz> template<size_t N> Node<_sz>::Node(/ * parameters * /) 
*  { 
*   // ... 
*  } 
*/ 

// Constructor initialising from C array, if you need to work with preexisting code. 
Node::Node(bool isBase, const int (&channelList)[_sz]) : _isBase(isBase) 
{ 
    std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList)); 
} 

// Constructor using std::array. 
Node::Node(bool isBase, const std::array<int, _sz>& channelList) 
    : _isBase(isBase), _channelList(channelList) 
{ 
    // Initialisation list handles everything. 
} 

// Note, however, that this will cause issues if the size of channelList doesn't 
// necessarily match the size of _channelList. To solve this, we can change Node as 
// follows: 
// (Note that delegation requires a C++11-compatible compiler.) 

#include <type_traits> // For std::integral_constant, std::true_type, and std::false_type. 

class Node { 
    bool _isBase; 
    std::array<int, _sz> _channelList; 

    // Actual constructors (C-style array): 
    template<size_t N> 
    Node(std::true_type x, bool isBase, const int (&channelList)[N]); 

    template<size_t N> 
    Node(std::false_type x, bool isBase, const int (&channelList)[N]); 

    // Actual constructors (std::array): 
    template<size_t N> 
    Node(std::true_type x, bool isBase, const std::array<int, N>& channelList); 

    template<size_t N> 
    Node(std::false_type x, bool isBase, const std::array<int, N>& channelList); 

    public: 
     // Public constructors, delegate to one of the actual constructors. 
     // C-style array: 
     template<size_t N> 
     Node(bool isBase, const int (&channelList)[N]); 

     // std::array: 
     template<size_t N> 
     Node(bool isBase, const std::array<int, N>& channelList); 

     // ... 
}; 

/* Now, these constructors are easy enough to make. 
* I'll provide an example using C-style arrays. To make versions that take a 
* std::array instead, change the parameter: 
*  const int (&channelList)[N] 
* to: 
*  const std::array<int, N>& channelList 
* The constructors should work properly with either kind of array. 
*/ 

// Check if passed array is smaller than or equal to _sz, or if it's larger.. 
template<size_t N> Node::Node(bool isBase, const int (&channelList)[N]) 
    : Node(std::integral_constant<bool, N <= _sz>{}, isBase, channelList) { } 

// N is smaller than or equal to _sz. 
template<size_t N> Node::Node(std::true_type x, bool isBase, const int (&channelList)[N]) 
    : _isBase(isBase) 
{ 
    // Copy channelList into _channelList. 
    std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList)); 
    // Fill empty space at the end of _channelList. 
    std::fill(&(_channelList[N]), std::end(_channelList), '\0'); 
} 

// N is larger than _sz. 
template<size_t N> Node::Node(std::false_type x, bool isBase, const int (&channelList)[N]) 
{ 
    // Handle appropriately. 
} 

這應該讓你得到你想要的功能。 [請注意,您也可以用上面的代表團,true_typefalse_type建設者,以填補C風格的數組以及std::array S,如果你需要使用它們。]


如果大小不固定:

#include <vector> 
#include <algorithm> 

class Node { 
    bool _isBase; 
    std::vector<int> _channelList; 

    public: 
     template<size_t N> 
     Node(bool isBase, const int (&channelList)[N]); 

    // ... 
}; 

template<size_t N> Node::Node(bool isBase, const int (&channelList)[N]) : _isBase(isBase) 
{ 
    _channelList.assign(std::begin(channelList), std::end(channelList)); 
} 

// You can also define a constructor that takes a std::array<int, N>, if you 
// so desire. Again, the only change needed is the parameter itself. 

作爲向量的長度可以在運行時改變,我們可以使用vector::assign分配空間並存儲的channelList整體。


不管_channelList是否被存儲爲C陣列,std::array,或std::vector,它相對容易定義getter和setter。

吸氣(整個事情):

// All of the following use this class definition, with comments identifying which 
// functions use which parts. 
// Note that the trailing "const" in each getter signature indicates that the function 
// cannot be used to modify the instance. It's optional, but useful to include. 
class Node { 
    // Return C array (either way). 
    int _channelListC[_sz]; 
    // Return std::array. 
    std::array<int, _sz> _channelListSArr; 
    // Return std::vector. 
    std::vector<int> _channelListSVec; 

    // Return C array the readable way. 
    typedef int _channelListC_t[_sz]; 
    // C++11 alternative typedef: 
    using _channelListC_t = decltype(_channelList); 
    // The C++11 version is safer, as "decltype(_channelList)" won't break if you change 
    // _channelList's implementation. 
    // If you need to return the entire array, it may be a good idea to make this a public 
    // typedef, so it's easier & safer to declare a variable you can return it to. 

    public: 
     // Return C array the ugly way. 
     const int (&getChannelListCUgly() const)[_sz]; 

     // Return C array the readable way. 
     const _channelListC_t& getChannelListCReadable() const; 

     // Return C array the readable C++11 way. 
     auto getChannelListCReadableCPP11() const -> const int(&)[_sz]; 

     // Return std::array. 
     const std::array<int, _sz>& getChannelListSArr() const; 

     // Return std::vector. 
     const std::vector<int>& getChannelListSVec() const; 
}; 

// Return C array: 
/* Note that you can't return an array from a function. However, you can return a pointer 
* or reference to an array, depending on whether you use * or & in the signature. 
*/ 
// The ugly way: 
const int (&Node::getChannelListCUgly() const)[_sz] 
{ 
    return _channelList; 
} 

// The readable way: 
const Node::_channelListC_t& Node::getChannelListCReadable() const 
{ 
    return _channelList; 
} 

// The new readable way, as of C++11: 
auto getChannelListCReadableCPP11() const -> const int(&)[_sz] 
{ 
    return _channelList; 
} 

// Return std::array: 
const std::array<int, _sz>& Node::getChannelListSArr() const 
{ 
    return _channelList; 
} 

// Return std:;vector: 
const std::vector<int>& getChannelListSVec() const 
{ 
    return _channelList; 
} 

注意,據我所知,C風格的數組以這種方式返回必須存儲在一個參考變量。

Node::_channelListC_t& arr = nodeInstance.getChannelListCUgly(); 

吸氣劑(單個元件):

// C array or std::array: 
int Node::getChannelListArrElement(int index) const 
{ 
    if (index < _sz) 
    { 
     // index is valid, return element. 
     return _channelList[index]; 
    } 
    else 
    { 
     // index >= _sz, and is invalid. 
     // Handle accordingly. 
    } 
} 

// std::vector: 
int Node::getChannelListVecElement(int index) const 
{ 
    if (index < _channelList.size()) 
    { 
     // index is valid. 
     return _channelList[index]; 
    } 
    else 
    { 
     // index is invalid. 
     // Handle accordingly. 
    } 
} 

可以定義爲使用上述構造整個事情一個setter。我建議首先使用std::fill()擦除_channelList的內容,然後將新陣列複製到_channelList。您可以使用單元素吸氣劑作爲基礎,爲單個元素定義一個setter。

二傳手(整個事情):

// Array (either type): 
// "CHANNEL_LIST_TYPE[N] channelList" is either "const int (&channelList)[N]" or 
// "std::array<int, N>& channelList". Remember to replace it with the correct one in the 
// actual code. 

// Public function. 
template<size_t N> 
void Node::setChannelListArr(CHANNEL_LIST_TYPE[N] channelList) 
{ 
    setChannelListArr(std::integral_constant<bool, N <= _sz>{}, channelList); 
} 

// Private function, N <= _sz. 
template<size_t N> 
void Node::setChannelListArr(std::true_type x, CHANNEL_LIST_TYPE[N] channelList) 
{ 
    std::fill(std::begin(_channelList), std::end(_channelList), '\0'); 
    std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList)); 
} 

// Private function, N > _sz. 
template<size_t N> 
void Node::setChannelListArr(std::false_type x, CHANNEL_LIST_TYPE[N] channelList) 
{ 
    // channelList is too large. Handle appropriately. 
} 


// std::vector: 
// "CHANNEL_LIST_TYPE[N]" is used as above, and should be replaced in your actual code. 
// Also note that you can easily modify this function to accept another vector, by 
// removing the template, making the parameter "const std::vector<int>& channelList", and 
// using "channelList.size()" in place of "N" when calling resize(). 
template<size_t N> 
void Node::setChannelListVec(CHANNEL_LIST_TYPE[N] channelList) 
{ 
    _channelList.resize(N); // Resize _channelList to have N elements. 
    std::fill(std::begin(_channelList), std::end(_channelList), '\0'); 
    std::copy(std::begin(channelList), std::end(channelList), std::begin(_channelList)); 
} 

二傳手(單件):

// Array (either type): 
void Node::setChannelListArrElement(int index, int value) 
{ 
    if (index < _sz) 
    { 
     _channelList[index] = value; 
    } 
    else 
    { 
     // index is invalid. Handle accordingly. 
    } 
} 

// std::vector: 
void Node::setChannelListVecElement(int index, int value) 
{ 
    if (index < _channelList.size()) 
    { 
     _channelList[index] = value; 
    } 
    else 
    { 
     // index is invalid. Handle accordingly. 
    } 
} 

// Alternative std::vector setter: 
void Node::setChannelListVecElement2(int index, int value) 
{ 
    if (index >= _channelList.size()) 
    { 
     // index is out of bounds. Resize vector to fit it. 
     _channelList.resize(index + 1, '\0'); 
    } 

    // Modify element. 
    _channelList[index] = value; 
} 

注意,這個答案假定channelList是空值終止的,因爲它似乎是。如果channelList是不是空終止,但你要停在第一個空元素填充_channelList,那麼你就必須做一些更多的工作,有可能使用while循環。


你可以找到大部分以上here的工作示例。這有點混亂,因爲它只是一個快速程序,我用它在輸入這個答案時測試各種東西。


[我對我可能錯過的任何錯誤和/或錯誤表示歉意。我相信我抓了他們,但可能仍然存在一些有]

[編輯:添加了一條說明如何使用固定大小的模板的構造與C數組。添加了C++ 11尾隨返回類型的方法,用於返回對數組的引用。添加了一個簡單的工作示例。] [編輯:爲向量添加了額外的單元素設置器。]

+0

請注意,'std :: array'也有一個成員函數'size()';由於尺寸是已知的,並作爲常量或模板參數存儲,我直接使用它來保持一致性。還要注意'array'和'vector'具有成員函數'begin()'和'end()',這樣'array_or_vector.begin()'等價於'begin(array_or_vector)','array_or_vector.end ()'相當於'end(array_or_vector)'。再次,我與後者一致。 –

1

賈斯汀時間的答案是在C++中實現這一點的正確方法(使用C++原生處理的數組和向量)

我只需要添加該解決方案,這是實現你試圖在C++做的(即使用字符數組)的正確方法。

有在你的代碼

  1. _channelList兩個問題是不依賴於Node對象,但在某種程度上是靜態成員。
  2. _channelList從未分配,因此它指向什麼。
  3. 不是一個真正的問題,但通常'\0'是一個字符串結束。好的,它映射到0,但您應該只使用0這裏

這裏有兩種解決方案。第一個是給_channelList一個固定的最大尺寸(最大意味着,如果傳遞的channelList較短,你會得到一個更短的清單,確定,但所分配的內存將仍然是最大的一個)。

// File Node.h 
#define MAXIMUM_CHANNELS 10 
class Node { 
    public: 
     Node(boolean isBase, int *channelList); 
     boolean isBase(); 
     int* getChannelList(); 

    private: 
     int _channelList[MAXIMUM_CHANNELS + 1]; // Last one is the terminator 
}; 

// File Node.cpp 
include "Arduino.h" 
#include "Node.h" 
#include <ESP8266WiFi.h> 
#include <WiFiUdp.h> 

Node::Node(boolean isBase, int *channelList) 
{ 
    _isBase = isBase; 
    int channelListLength; 
    // Get channel list lenght 
    for (channelListLength = 0; channelList[channelListLength] != 0; channelListLength++); 

    if (channelListLength > MAXIMUM_CHANNELS) 
     channelListLength = MAXIMUM_CHANNELS; 

    int i; 
    for (i = 0; i < channelListLength; i++) 
     _channelList[i] = channelList[i]; 
    _channelList[channelListLength] = 0; // Enforce the last one to be a 0 
} 

boolean Node::isBase(){ 
    return _isBase; 
} 

int* Node::getChannelList(){ 
    return _channelList; 
} 

第二種方法爲數組動態分配內存。但是,當你完成對象(在析構函數中)時,你應該處理它。這意味着如果您創建Node變量,那麼您就可以(例如,Node mynode;)。但是,如果您動態分配它(使用Node *p_mynode = new Node();,則完成後您需要在其上調用delete

// File Node.h 
class Node { 
    public: 
     Node(boolean isBase, int *channelList); 
     ~Node(); // destructor (called at object destruction) 
     boolean isBase(); 
     int* getChannelList(); 

    private: 
     int *_channelList; 
}; 

// File Node.cpp 
include "Arduino.h" 
#include "Node.h" 
#include <ESP8266WiFi.h> 
#include <WiFiUdp.h> 

Node::Node(boolean isBase, int *channelList) 
{ 
    _isBase = isBase; 
    int channelListLength; 
    // Get channel list lenght 
    for (channelListLength = 0; channelList[channelListLength] != 0; channelListLength++); 

    _channelList = (int*)malloc((channelListLength+1)*sizeof(int)); 
    if (_channelList != NULL) 
    { 
     int i; 
     for (i = 0; i <= channelListLength; i++) 
      _channelList[i] = channelList[i]; 
     // No need to enforce the last one to be a 0 
    } 
} 

~Node() 
{ 
    free(_channelList); 
} 

boolean Node::isBase(){ 
    return _isBase; 
} 

int* Node::getChannelList(){ 
    return _channelList; 
} 

但是請注意,如果malloc失敗,你將有一個NULL指針。從理論上講,它不應該失敗,除非你用完ram ...

只是還有一件事。使用以0結尾的int數組並不是一個好主意,因爲如果你有這樣的{ 15, 3, 0, 5, 10 },然後終止符,你只會得到前兩個數字。明確地告訴數組大小會更好(並將其保存在對象中)

// File Node.h 
class Node { 
    [...] 
    private: 
     int *_channelList; 
     int _channelListLength; 
}; 

// File Node.cpp 
Node::Node(boolean isBase, int *channelList, int channelListLength) 
{ 
    _isBase = isBase; 

    _channelListLength = channelListLength; 
    _channelList = (int*)malloc((_channelListLength)*sizeof(int)); 
    if (_channelList != NULL) 
    { 
     int i; 
     for (i = 0; i <= _channelListLength; i++) 
      _channelList[i] = channelList[i]; 
    } 
} 

... 

int Node::getChannelListLength(){ 
    return _channelListLength; 
}