Статическая константа double в С++

Это правильный способ использования статической переменной const? В моем классе высшего уровня (Форма)

#ifndef SHAPE_H
#define SHAPE_H

class Shape
{
public:

    static const double pi;
private:
    double originX;
    double originY;
};

const double Shape::pi = 3.14159265;

#endif

А позже в классе, расширяющем Shape, я использую Shape::pi. Я получаю ошибку компоновщика. Я переместил const double Shape::pi = 3.14... в файл Shape.cpp, после чего моя программа скомпилировалась. Почему это происходит? благодаря.


person Crystal    schedule 05.05.2010    source источник


Ответы (7)


Поскольку const double Shape::pi = 3.14159265; является определением Shape::pi, а C++ допускает только одно определение символа (называемое правилом одного определения, который вы можете увидеть в форме аббревиатуры ODR). Когда определение находится в заголовочном файле, каждая единица перевода получает собственное определение, которое нарушает это правило.

Переместив его в исходный файл, вы получите только одно определение.

person R Samuel Klatchko    schedule 05.05.2010
comment
У меня такая же проблема. Проблема в том, что если это библиотека, и я хочу, чтобы мой пользователь видел значение Shape::pi в файле .h, но не содержимое cpp (т.е. другое определение функции). Что я должен делать? - person Peter Lee; 24.05.2011

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

Это также прискорбно, потому что, будучи алгебраическим значением, иметь непосредственное значение под рукой может быть удобно для оптимизации, а не для загрузки из глобальной переменной. (Разница, вероятно, будет несущественной, хотя.)

Однако есть решение!

class Shape
{
public:
    static double pi()
        { return 3.14159265; }

private:
    double originX;
    double originY;
};

Определения встроенных функций, включая статические, разрешены внутри блока class{}.

Кроме того, я рекомендую использовать M_PI от <math.h>, который вы также должны получить от <cmath>.

person Potatoswatter    schedule 06.05.2010
comment
дополнительно: шаблоны имеют разную связь, поэтому вы можете использовать шаблон, чтобы сделать определение видимым - person justin; 06.05.2010
comment
@Justin: я не думаю, что template даст вам здесь что-нибудь, кроме дополнительных осложнений. - person Potatoswatter; 06.05.2010
comment
шаблон может все усложнить, но он потенциально дает вам исключение из правила одного определения для статического члена данных шаблона класса (среди других исключений ODR). См. 3.2/5 и 14.5.1.3 стандарта C++. - person Michael Burr; 06.05.2010
comment
@Michael: Но для идентификации этого члена данных шаблона при использовании требуется список аргументов шаблона, который не менее утомителен (и немного менее понятен), чем вызов функции. - person Potatoswatter; 06.05.2010
comment
+1 за решение (которое я бы явно объявил как inline) - person Andre Holzner; 03.12.2011
comment
M_PI удобен, но не является частью стандарта C++. - person quant_dev; 25.01.2015
comment
@quant_dev Действительно, это часть POSIX. Однако на практике многие платформы, отличные от POSIX, также предоставляют его. - person Potatoswatter; 26.01.2015

Если бы у вас был способ добавить флаг C++0x в ваш компилятор, вы могли бы сделать:

ifndef SHAPE_H
#define SHAPE_H

class Shape
{
public:

    static constexpr double pi = 3.14159265;
private:
    double originX;
    double originY;
};

#endif

В C++0x вы можете использовать константные выражения для типов, отличных от целочисленных. Это позволяет вам объявить и определить вашу постоянную переменную.

person Benjamin    schedule 02.02.2013
comment
Здравствуйте, согласно en.cppreference.com/w/cpp/language/constexpr constexpr введен в С++ 11, поэтому не должен ли флаг компилятора быть С++ 11, 14, 17 или 20, но особенно не С++ 0x? - person b.g.; 20.04.2021
comment
Моему отзыву уже 8 лет. В то время не было C++17. Однако вы можете редактировать ответ. - person Benjamin; 20.04.2021

Это происходит потому, что вы не можете определить Shape::pi более одного раза. Он определяется один раз, когда вы включаете Shape.h в Shape.cpp, а затем снова каждый раз, когда вы используете Shape.h в другом файле cpp. Когда вы пытаетесь связать свою программу вместе, компоновщик будет блевать из-за нескольких определений.

person Joseph Lisee    schedule 05.05.2010

Строка const double Shape::pi = 3.14159265; должна быть в вашем файле Shape.cpp. Заголовочный файл предназначен для объявления переменных. Вы можете определить переменную только один раз, поэтому это нужно сделать в файле .cpp. Заголовочный файл говорит, как использовать эти переменные и функции, файл cpp говорит, что делать.

person zmbush    schedule 05.05.2010

Для примитивных типов данных (например, int, double, но не char[]) вы также можете определить константу в определении класса в заголовочном файле, например:

class Shape
{
public:
    static const double pi = 3.14159265;

private:
    double originX;
    double originY;
};

Это позволит лучше оптимизировать компилятор.

Редактировать: как Деннис указал ниже, это разрешено только для целочисленных типов, а не для типов данных типа double или float (однако некоторые компиляторы разрешают это).

person Oliver    schedule 06.05.2010
comment
Вы можете сделать это только для целочисленных и перечислимых типов. Типы с плавающей запятой не допускаются. - person Dennis Zickefoose; 06.05.2010
comment
@ Деннис: Ой! Ты прав! Меня обманул компилятор (gcc и icc позволили инициализировать static const double внутри класса)... - person Oliver; 06.05.2010

Реализуйте функцию, которая возвращает индекс значения в список, если он существует. В противном случае верните -1, если значения нет. Если одно и то же значение присутствует в списке более одного раза, первое значение удаляется снизу.

public static intfindFromLast (List <Double> l, double value ) {///…}
person besarta saraqini    schedule 17.06.2017