Массив неявно преобразуется в контейнерный класс при передаче в качестве аргумента в C ++ - PullRequest
1 голос
/ 18 февраля 2012

Я работал над проектом и, играя с кодом, я обнаружил следующее необычное явление.

У меня есть два класса. Первый содержит три числа с плавающей точкой в ​​массиве, представляющем декартовы координаты, и определяет метод для получения этих точек;

class foo
{
protected:
    float m_Coordinates[3];

public:
    foo(float coordinates[3]);
    void GetPoints(int resultArray[]);
};

foo::foo(int coordinates[3])
{
    std::copy(coordinates, coordinates+3, m_Coordinates);
}

void foo::GetPoints(float resultArray[])
{
    std::copy(m_Coordinates, m_Coordinates+3, resultArray);
}

Второй класс также хранит массив с плавающей точкой, но его конструктор использует foo в качестве класса-оболочки для передачи значений:

class bar
{
protected:
    float m_MoreCoordinates[3];

public:
    bar(foo f);
};

bar::bar(foo f)
{
    f.GetPoints(m_MoreCoordinates);
    //m_MoreCoordinates is passed by reference, so the values in
    //m_MoreCoordinates are equal to the values in f.m_Coordinates
    //after this line executes
}

Пожалуйста, игнорируйте тот факт, что подход, который я применил к этому коду, просто ужасен. Это началось как эксперимент с использованием массивов. Передавая их в качестве аргументов, получая их в качестве возвращаемых типов и т. Д.

OK. Здесь я заметил нечто странное. Если я объявлю массив с плавающей точкой и передам их в качестве аргумента конструктору bar, компилятор сгенерирует экземпляр класса foo и передаст его в bar для меня. Смотрите пример кода ниже:

int main(int argv, char** argc)
{
    float coordinates[] = {1.0f, 2.1f, 3.0f};


    //Here the compiler creates an instance of class foo and passes 
    //coordinates as the argument to the constructor. It then passes 
    //the resulting class to bar's constructor.
    bar* b = new bar(coordinates);

    //Effectively, the compiler turns the previous line into
    //bar* b = new bar(foo(coordinates));

    return 0;
}

Когда я увидел это, я подумал, что это довольно приятная особенность кода, и подумал, как и почему это произошло. Безопасно ли это делать? Я не понимаю, как это работает, и поэтому я не хочу зависеть от этого. Если бы кто-то мог объяснить, как это работает, я был бы очень признателен.

Edit: Спасибо Mankarse за указание, как преобразование будет выполнено в основном. Изначально у меня было:

//Effectively, the compiler turns the previous line into
//bar* b = new bar(*(new foo(coordinates)));

Ответы [ 2 ]

2 голосов
/ 18 февраля 2012

Как вы уже догадались, компилятор неявно создает объект foo и передает его bar.Как правило, это считается немного опасным, так как foo создается без знания, чтобы избежать этого, вы можете объявить foo конструктор как explicit.В этом случае компилятор не будет неявно создавать foo из массива с плавающей точкой, и вы получите ошибку компилятора.

1 голос
/ 18 февраля 2012

Когда вы думаете об этом, вы используете это все время.Рассмотрим следующее:

void foo(std::string argument);

Затем предположим, что вы вызываете эту функцию, используя строковый литерал:

foo("argument");

Это то же самое, что и

std::string argument("argument");
foo(argument);
* 1009довольно полезная функция.
...