c++ const членска функция

Чета книга, наречена „Ефективен C++, второ издание“ и в нея се говори за константни функции-членове и как имате побитова константност и концептуална константност.

Той казва, че повечето компилатори ще работят с побитова константност, което е, че не можете да променяте членовете на данни на обект вътре в константна функция член.

След това има пример за член-функция, която изглежда не действа побитово в const теста.

Става така:

#include "stdafx.h"
#include <string>
#include <iostream.h>

using namespace std;

class mystring
{

public:
    mystring(const char* value);

    operator char *() const { return data; }

private:
    char * data;
};

mystring::mystring(const char * value)
{

    mystring::data = const_cast<char*>(value);
}


int main(int argc, char* argv[])
{
    const mystring s = "Hello";

    char * nasty = s;

    *nasty = 'M';

    printf("s: %c", s);

    return 0;
}

Когато това се изпълни, в книгата ми пише, че трябва да ви позволи да промените стойността на s, въпреки че е const. Това е така, защото данните char* сочат към същото, както сочи стойността const char*. *data в този случай не е const.

Въпреки това, опитвайки се да изпълни това в MS VC++ 6.0, той хвърля нарушение на достъпа на ред *nasty = 'M';

Може ли някой да обясни какво се случва? Мисля, че съм пропуснал нещо?

За мен изглежда, че тъй като имаме const mystring s, не би трябвало да можем да го променим, но тогава това, което пише в книгите, изглежда неудобно.


person Tony The Lion    schedule 02.07.2010    source източник
comment
Това не е от значение за въпроса, но имайте предвид, че това издание на книгата и Visual Studio 6 датират от дните преди езикът да бъде стандартизиран, когато много общи C++ идиоми не са били обмислени още. Ако искате да научите как C++ се използва днес, ще ви е по-добре с третото издание (което аз напълно препоръчвам) и модерен компилатор. Visual Studio Express е безплатен за изтегляне, ако искате да се придържате към Microsoft.   -  person Mike Seymour    schedule 02.07.2010


Отговори (4)


Нарушението на достъпа е, защото се опитвате да промените литерал на низ. Вашият код е еквивалентен на:

char * p = "Hello";
* p = 'M';

което е незаконно както в C, така и в C++ - нищо общо с const членските функции.

person Community    schedule 02.07.2010
comment
така че предполагам, че низовият литерал е неизменен обект? - person Tony The Lion; 02.07.2010
comment
@Tony: не е дефинирано дали в дадена реализация низов литерал е наистина неизменен обект от гледна точка на стандарта. Стандартът само ви казва на този обект, че е недефинирано поведение за модифициране на обекти, които са постоянни (в сравнение с непостоянни обекти, достъпни чрез постоянна препратка – вижте моя отговор) - person David Rodríguez - dribeas; 02.07.2010
comment
@Tony Не знам за това - всички стандарти на C и C++ казват, че опитът за модифициране води до недефинирано поведение. - person ; 02.07.2010
comment
Обикновено семантиката за копиране е препоръчителна. И в този случай копирането на низа във вашия конструктор може да бъде най-добрият начин за предотвратяване на този тип проблем. От друга страна, промяната на члена на данните да бъде const и оператора за прехвърляне да връща const char* също ще работи. И двете ще направят нещата последователни. Вашият конструктор не трябва да приема, че може да отхвърли const. Това е друг урок, който трябва да се научи с този пример, освен поведението на const. - person Craig Wright; 02.07.2010

Получавате нарушение на достъпа само защото указателят char* е към низов литерал. Промяната на низов литерал е недефинирано поведение (AV във вашия случай), няма нищо общо с константната коректност.

person sharptooth    schedule 02.07.2010
comment
@Тони: използвай std::strings: std::string salute = "Hi"; salute = "Good bye";. Решението на C би било да копирате и модифицирате копието, да използвате литерала за създаване на неконстантен обект... char text[] = "Hi!"; text[0] = 'B'; text[1] = 'y'; text[2] = 'e'; --обърнете внимание, че масивите не могат да променят размера, редактирането на text до Чао работи само защото Здравейте! и чао вземете същото количество памет. Също така имайте предвид, че е дефиниран като char text[], а не char *text, масивите и указателите не са еднакви. - person David Rodríguez - dribeas; 02.07.2010

Това, което правите, е недефинирано поведение. Отхвърляте постоянството и се опитвате да промените постоянна стойност. Ако този пример е копиран такъв, какъвто е от книгата, тогава книгата е грешна.

Това, което е законно да се прави с const_cast<>, е премахването на константността от постоянен указател/препратка към неконстантен обект:

int i = 5;
int const & ir = i;
const_cast<int&>(ir) = 7; // valid
int const * ip = &i;
*const_cast<int*>(ip) = 9; // valid

const int c = 11;
int const & cr = c;
const_cast<int&>(cr) = 13; // Undefined behavior

Причината, поради която не можете да отхвърлите константността от препратка към реален константен обект, е, че компилаторът може да реши да постави обекта в памет само за четене, в който случай операцията може да се провали по различни начини (убиване на приложението, игнориране промяната...)

person David Rodríguez - dribeas    schedule 02.07.2010

Във вашия пример вие не променяте s, а се опитвате да промените паметта, към която сочи членска променлива на s. Тъй като ви е позволено да поставите const на много места, трябва да внимавате какво всъщност декларирате const.

Вашата членска функция operator char*() const няма право да променя никоя членска променлива. Опитайте operator char *() const { data = "something else"; return data; } и вашият компилатор ще ви каже, че нямате право да променяте data. В този случай обаче вие ​​просто връщате data без модификация. Това е позволено. Дори ви е позволено да промените паметта, към която сочи data, както в operator char *() const { *data = 'M'; return data; }. Това обаче се проваля във вашия контекст, тъй като data сочи към низов литерал, който не ви е разрешено да променяте.

person Malte Clasen    schedule 02.07.2010
comment
Защо това е валидно: const char* v = Hello; v = Тони; ? - person Tony The Lion; 02.07.2010
comment
@Tony: v е указател към константни данни, първоначално сочещ към низов литерал, съдържащ "Hello". v = "Tony" го променя, за да сочи към различен низов литерал. Не променя първия низ. - person Mike Seymour; 02.07.2010
comment
Внимавайте къде да поставите, че const: char * const v = "Hello"; v = "Tony"; е постоянен указател към неконстантни данни, така че присвояването води до грешка на компилатора. - person Malte Clasen; 02.07.2010