Поймите, что const корректна с указателями в C

#c #c 11

Вопрос:

У меня есть следующий тестовый код:

 class A
{
    public:
    void funcA()
    {
        std::cout << "banana" << std::endl;
    }
};

class B
: public A
{
    public:
    A t;

    void funcB() const
    {
       t.funcA();
    }
};

int main(int argCount, char *args[])
{
   B tst;
   tst.funcB();
}
 

Где funcA нет const определения и funcB есть const определение.

При компиляции кода я получаю ошибку:

 passing ‘const A’ as ‘this’ argument discards qualifiers [-fpermissive]
 

Это должно произойти, потому что я вызываю внутри const функции non-const функцию.

Однако, когда я это сделаю:

 class B
: public A
{
    public:
    A* t;

    void funcB() const
    {
       t->funcA();
    }
};
 

Код компилируется и работает. Это почему? Я просто компилируюсь с g -g main.cpp , и мне не хватает флагов для этого? или такого поведения следует ожидать?

Комментарии:

1. Вы хотите const A *t тогда вместо A *t этого . Помните, что вы изменяете объект t , на который указывает объект, а не t на него самого, поэтому компилятор считает, что это нормально.

2.Почему у вас есть A и как базовый класс, B и как член B ? Вы вообще не используете базовый класс, поэтому, возможно, удалите его из фрагментов (чтобы сделать их менее запутанными) и сохраните только элемент t . Первый фрагмент не будет компилироваться, потому t что (фактический экземпляр A встроен в экземпляр B ) const , как и окружающий B экземпляр, на который this указывает. Во втором фрагменте, t указывает на не const экземпляр A (хотя сам указатель t есть const ), на котором вы можете (конечно) вызывать произвольные не — const методы.

3. Это t само const по себе , а не объект, на который указывает t .

4. learncpp.com/cpp-tutorial/pointers-and-const

5. std::экспериментальный::propagate_const может иметь ожидаемое поведение.

Ответ №1:

Вы не изменяете переменную-член указателя, вы изменяете значение, на которое она указывает, поэтому она компилируется.

t Переменная — это указатель, указывающий на переменную типа A . Выполнение t->funcA(); может изменить значение, на которое указывает переменная, но оно не изменяет сам элемент указателя, и это все, что требуется компилятору для применения здесь.

Если бы вы изменили фактический указатель таким образом, эта функция не компилировалась бы.

 void funcB() const 
{
    t = nullptr; //Will not compile
}
 

Если вы хотите запретить кому-либо изменять переменную A с помощью указателя, просто объявите указатель как const. Это предотвратит изменение значения, на которое оно указывает.

 const A* t;
 

Ответ №2:

Случай I: Объяснение вашего 1-го фрагмента кода

Когда мы пишем

 void funcA();  
 

это означает funcA() , что имеет неявный параметр, типом которого является A *const , т. Е. Указатель const на неконстантный объект. Это, в свою очередь, означает, что мы можем вызывать эту функцию только для неконстантных объектов. Если вы попытаетесь вызвать эту функцию-член для объекта const A, вы получите указанную ошибку.

Чтобы показать мою точку зрения, давайте попробуем вызвать funcA объект const A. Код выглядит следующим образом:

 #include <iostream>

class A
{
    public:
    void funcA()
    {
        std::cout << "banana" << std::endl;
    }
};

int main()
{
  A nonconstobject;
  nonconstobject.funcA(); //this works
  
  const A constobject;  //note the const here
  constobject.funcA(); //this gives error: passing ‘const A’ as ‘this’ argument discards qualifiers
}
 

Приведенный выше фрагмент кода выдает ту же ошибку, о которой вы упомянули, как видно здесь.

И это именно то, что происходит в вашем 1-м фрагменте кода. Теперь я собираюсь пошагово объяснить, что происходит в вашем 1-м фрагменте кода:

Шаг 1. Вы funcB(); объявили как функцию-член const. Это означает, что его собственный неявный параметр имеет тип const B *const , т. Е. Указатель const на объект const B. Это, в свою очередь, означает, что элементы данных(включая элемент данных t) B сами являются постоянными.

Шаг 2. Теперь, поскольку t это const и, как я сказал в своем пользовательском примере, вы не можете вызывать функцию-член nonconst для объекта const. Таким образом, вы получаете упомянутую вами ошибку.

Случай 2: Объяснение вашего 2-го фрагмента кода

Во втором случае вы изменили элемент данных t на тип A* , который является указателем на объект.

Теперь давайте рассмотрим пошагово, что происходит в этом случае:

Шаг 1. Вы funcB(); объявили как функцию-член const. Это означает, что его собственный неявный параметр имеет тип const B *const , т. Е. Указатель const на объект const B. Это, в свою очередь, означает, что элементы данных(включая элемент данных t) B сами являются постоянными. Разница на этот раз в том, что t станет постоянным указателем на A, т. е. t имеет тип A* const

Шаг 2. Снова funcA имеет неявный параметр, который имеет тип A *const , так funcA как не является функцией-членом const. И этот тип точно соответствует тому, что вы передаете с помощью вызова t->funcA(); . Так что в этом случае вы не получите никакой ошибки.