Создать объект, только если какое-то условие, в противном случае вернуть nullptr - PullRequest
1 голос
/ 09 марта 2012

Я хочу создать объект только в том случае, если применяются некоторые условия, в противном случае повторно введите nullptr.Вот как я бы сделал это в Delphi (2009 +):

function GetGen(n : integer) : Generics.Collections.TList<Integer>;
var
i : integer;
begin
  result := nil;
  if n > 0 then begin
      result := Generics.Collections.TList<Integer>.Create;
      for i := 0 to n - 1 do result.Add(i);
  end;
end;

procedure TestGenList(n : integer);
var
   aInt : integer;
   aGen : Generics.Collections.TList<Integer>;
begin
   aGen := GetGen(n);
   if aGen = nil then begin
      WriteLn('No generic created!');
      Exit;
   end;
   WriteLn(Format('Size: %d', [aGen.Count]));
   for aInt in aGen do Write(Format('%d ', [aInt]));
   aGen.Free; //will clear integers
end;

procedure TestGen
begin
    TestGenList(0);
    Readln;
    TestGenList(5);
    Readln;
end.

Вот как я мог бы сделать это в C ++:

unique_ptr<vector<int>> GetUniquePrtVec(int n){
    if (n < 1) return(nullptr); //create only if correct input is given
    unique_ptr<vector<int>> result (new vector<int>);
    for (int i = 0 ; i != n; i++){
        result->push_back(i);
    }
    return(move(result));
}

void TestPtrVec(int n){
    unique_ptr<vector<int>> vec = GetUniquePrtVec(n);
    if (vec == nullptr){
        cout << "No vector created" << endl;
        return;
    }
    cout << endl << vec->size() << endl;
    for_each(vec->begin(), vec->end(), [](int n){cout << n << " " << endl;});
    vec->clear(); //clear vector
    vec.reset(nullptr);
}

void testVec3(){
    TestPtrVec(0);
    TestPtrVec(5);
}

Мой вопрос о правильной идиоме.Ребята, опытные программисты на С ++ (потому что я новичок, просто изучаю язык), сделайте это так?Если нет, то как бы вы это сделали?

Спасибо.

Ответы [ 5 ]

1 голос
/ 09 марта 2012

Кажется, вам нужно что-то вроде boost::optional.Вот пример его использования:

optional<char> get_async_input()
{
    if ( !queue.empty() )
        return optional<char>(queue.top());
    else return optional<char>(); // uninitialized
}

void receive_async_message()
{
    optional<char> rcv ;
    // The safe boolean conversion from 'rcv' is used here.
    while ( (rcv = get_async_input()) && !timeout() )
        output(*rcv);
}

Для получения дополнительной информации см. Документацию boost.

1 голос
/ 09 марта 2012

лично я считаю, что наличие указателя на вектор немного необходимо, мне кажется, что вы могли бы просто вернуть пустой вектор или даже выдать ошибку неверного аргумента. Полное нулевое возвращаемое значение немного взломано, и теперь вам приходится управлять некоторым объемом памяти из-за этого.

Лично я бы предпочел

std::vector<int> get_vec(int n){
    std::vector<int> result;
    if(n < 1) return result;
    result.reserve(n);
    for (int i = 0 ; i != n; i++){
        result.push_back(i);
    }
    return result; 
}

или

std::vector<int> get_vec(int n){
    if(n < 1) throw std::invalid_argument("n must be greater than 1");
    std::vector<int> result;
    result.reserve(n);
    for (int i = 0 ; i != n; i++){
        result.push_back(i);
    }
    return result;
}


void test(int n){
    try{
    std::vector<int> vec = get_vec(n);
    catch(const std::exception& e)
    {
        std::cerr << "No vector created: " << e.what() << std::endl;
        return;
    }

//etc. .  .
1 голос
/ 09 марта 2012

ИМХО, лучший способ для вашего примера - просто вернуть std::vector по значению и просто вернуть пустой, если ввод неверен.

std::vector<int> get_vec(int n){
  std::vector<int> ret;
  for(unsigned i=0; i < n; ++i)
    ret.push_back(i);
  return ret; // will be empty for (n < 1)
              // and will be moved if (n >= 1)
}

Одна вещь, которую вам нужно выучить: вам не нужно явно std::move, если вы возвращаете локальную переменную. Просто верните по значению. Если копирование возможно, это будет сделано (RVO / NRVO). Если по какой-то причине это невозможно, он сначала попытается удалить его, прежде чем скопировать. Учтите, однако, что член локальной переменной не будет перемещен автоматически, он же

struct object{ std::vector<int> member; };

std::vector<int> foo(){
  object o;
  // ...
  return o.member; // no move, no copy elision, plain old copy
}

Теперь ваша вторая функция также может быть улучшена и уменьшена:

void try_vec(int n){
  auto vec = get_vec(n); // will elide copy or simply move
  for(auto& x : vec) // will not loop if vector is empty
    std::cout << x << ' '; // why space and newline?
  std::cout << "\n"; // don't use std::endl, it also flushes the stream
}

А из вашей исходной функции:

vec->clear(); //clear vector
vec.reset(nullptr);

Не требуется, в этом вся причина умных указателей и контейнеров управления ресурсами. Они уничтожат то, что им принадлежит, когда они выйдут за рамки.

0 голосов
/ 09 марта 2012

Я немного не знаком с этим синтаксисом, но я думаю, он мне подходит. Хотя почему бы просто не использовать указатели с обычным синтаксисом c +?

vector<int> GetUniquePrtVec(int n)
{
    if (n < 1) 
        return null;

    vector<int>* result = new vector<int>;
    for (int i = 0 ; i != n; i++){
        result->push_back(i);
    }
    return (result);
}

Хотя я никогда не использовал векторный указатель. Обычно, когда я создаю вектор, я передаю его функции по ссылке, например:

vector<int> myVec;
bool bSuccess = PopulateVec(n, myVec);

vector<int>* PopulateVec(int inNum, vector<int>& inVec)
{
    if (inNum< 1)
        return false;

    for (int i = 0 ; i != inNum; i++)
    {
        inVec->push_back(i);
    }

    // inVec is "returned" by reference
    return true
}
0 голосов
/ 09 марта 2012

Использование исключений или стирание типов, возвращение NULL - это способ действий C, а не C ++.

Также вы используете семантику перемещения, но не возвращаете r-значение, оно не будет работать так.

...