C++ `const`对象的构造函数

C++ `const`对象的构造函数,c++,constructor,arguments,constants,C++,Constructor,Arguments,Constants,考虑以下代码: #include <iostream> class test { public: test( char *arg ) : _arg( arg ) {} char *_arg; }; int main( ) { char *txt1 = "Text one"; // Ignore this warning. const char *txt2 = "Text two"; test

考虑以下代码:

#include <iostream>

class test
{
public:
    test( char *arg )
    :   _arg( arg )
    {}  
    char *_arg;
};

int main( )
{
    char *txt1       = "Text one";  // Ignore this warning.
    const char *txt2 = "Text two";

    test       t1( txt1 );  // Normal case, nothing new.
    const test t2( txt2 );  // Since object is const, I'd like to be able to pass a const argument in.
}
然后,我尝试添加第二个构造函数,以测试编译器是否可以为
t2
、我的
const
对象选择正确的构造函数:

    test( const char *arg )
    :   _arg( arg )
    {}  
但错误是:

error: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]
  : _arg( arg )
因此,将我的对象声明为
const
也不会使其字段
const


如何正确声明我的类构造函数,以便它能够处理常量和非常量对象的创建?

这是不可能的。仅仅因为对象是const,并不意味着它保证不会使用
char*
来修改其值。构造函数可以对参数进行变异,而类外的代码可以在其内容公开时修改其内容。考虑下面的例子。

struct Test {
    char* buffer;

    Test(char* buffer):
        buffer(buffer)
    {
        buffer[0] = 'a';
    }

    char* get() const {
        return buffer;
    }
};

int main(int argc, char* argv[]) {
    std::string s{ "sample text" };

    const Test t(s.data());

    t.get()[1] = 'b';
    t.buffer[2] = 'c';

    std::cout << s << '\n';
}
struct测试{
字符*缓冲区;
测试(字符*缓冲区):
缓冲区(缓冲区)
{
缓冲区[0]=“a”;
}
char*get()常量{
返回缓冲区;
}
};
int main(int argc,char*argv[]){
std::字符串s{“示例文本”};
常数测试t(s.数据());
t、 get()[1]=“b”;
t、 缓冲区[2]=“c”;

STD::CUT< P>它不能做。仅仅因为对象是const,并不意味着它保证<代码> char */COD>不被用来修改它的值。构造函数可以改变参数,并且类外的代码可以在它被暴露时修改它的内容。请考虑下面的例子。
struct Test {
    char* buffer;

    Test(char* buffer):
        buffer(buffer)
    {
        buffer[0] = 'a';
    }

    char* get() const {
        return buffer;
    }
};

int main(int argc, char* argv[]) {
    std::string s{ "sample text" };

    const Test t(s.data());

    t.get()[1] = 'b';
    t.buffer[2] = 'c';

    std::cout << s << '\n';
}
struct测试{
字符*缓冲区;
测试(字符*缓冲区):
缓冲区(缓冲区)
{
缓冲区[0]=“a”;
}
char*get()常量{
返回缓冲区;
}
};
int main(int argc,char*argv[]){
std::字符串s{“示例文本”};
常数测试t(s.数据());
t、 get()[1]=“b”;
t、 缓冲区[2]=“c”;
标准::cout
如何正确声明我的类构造函数,以便它能够处理常量和非常量对象的创建

在构造过程中,对象不是
const
,否则构造函数将无法初始化对象(因为所有数据成员都是
const

因此,构造函数不能是const限定的,也不能将构造函数重载用于
const
对象

现在,您可以重载参数,但在构造过程中,您的数据成员始终具有类型
char*
,尽管在符合常量的测试实例中使用时,它被限定为
char*const
(而不是
const char*

选项包括:

  • 重载参数类型的构造函数,并始终存储
    char*
    。如果传递了
    const char*
    ,则必须复制它(并且您有责任知道您拥有并且必须释放此内存)

    在这个方案中,您依赖于保持指针私有,并使用const限定的访问器来阻止通过const限定的对象更改指针的内容

    同样,您需要手动执行此操作,因为
    char*const
    const char*
    是不同的类型,因为指向类型的constness与指针的constness无关:拥有类的const实例只会停止您对指针的变异,而不是它指向的字符

  • 重载构造函数并始终存储一个
    const char*
    。这样可以避免复制,但如果有时需要更改指向的字符,则显然不起作用

  • 只需编写不同的可变字符串和不可变字符串类

  • <>如果有帮助的话,考虑一下:

    template <typename T> struct test {
        T* p_;
        test(T *p) : p_(p) {}
    };
    template <typename T> test<T> mktest(T *p) { return {p}; }
    
    给出
    a
    类型
    test
    ,和
    b
    类型
    test
    ,并且这些类型不相同,不可转换,在语言中与任何其他类型
    T
    test
    没有更密切的关系

    如何正确声明我的类构造函数,以便它能够处理常量和非常量对象的创建

    在构造过程中,对象不是
    const
    ,否则构造函数将无法初始化对象(因为所有数据成员都是
    const

    因此,构造函数不能是const限定的,也不能将构造函数重载用于
    const
    对象

    现在,您可以重载参数,但在构造过程中,您的数据成员始终具有类型
    char*
    ,尽管在符合常量的测试实例中使用时,它被限定为
    char*const
    (而不是
    const char*

    选项包括:

  • 重载参数类型的构造函数,并始终存储
    char*
    。如果传递了
    const char*
    ,则必须复制它(并且您有责任知道您拥有并且必须释放此内存)

    在这个方案中,您依赖于保持指针私有,并使用const限定的访问器来阻止通过const限定的对象更改指针的内容

    同样,您需要手动执行此操作,因为
    char*const
    const char*
    是不同的类型,因为指向类型的constness与指针的constness无关:拥有类的const实例只会停止您对指针的变异,而不是它指向的字符

  • 重载构造函数并始终存储一个
    const char*
    。这样可以避免复制,但如果有时需要更改指向的字符,则显然不起作用

  • 只需编写不同的可变字符串和不可变字符串类

  • <>如果有帮助的话,考虑一下:

    template <typename T> struct test {
        T* p_;
        test(T *p) : p_(p) {}
    };
    template <typename T> test<T> mktest(T *p) { return {p}; }
    

    给出了
    a
    类型
    test
    ,和
    b
    类型
    test
    ,并且这些类型不相同,不可转换,在语言上与任何其他类型
    T
    test
    没有更密切的关系。您遇到的问题更深一些。这是一种设计的指示问题

    您只想公开API的一部分。您说对于
    const
    对象,您将只调用
    const
    方法和f
    #include <iostream>
    using namespace std;
    
    struct A {
        void f1() const {cout << "foo\n"; }
        void f2() {cout << "bar\n"; }
    };
    
    struct readonlyA {
        readonlyA(const A& a) : _a(a) {};
        void f1() const {_a.f1();};
    private:
        const A& _a;
    };
    
    int main() {
        A a;
        readonlyA roA(a);
        a.f2();
        roA.f1();
        roA.f2(); // error
        return 0;
    }
    
    class test
    {
    public:
        test( char *arg )
        :   _arg( arg )
        {}  
        char *_arg;
    };
    
    class immutable_test
    {
    public:
        immutable_test(char const* arg)
            :_arg(arg)
        {}
    
        char const* _arg;
    };
    
    int main( )
    {
        char *txt1       = "Text one";  // Ignore this warning.
        const char *txt2 = "Text two";
    
        test           t1( txt1 );
        immutable_test t2( txt2 );
    }
    
        class test
        {
             public:
                test(const char *arg)
                : _arg(arg)
             {}
             const char *_arg;
        };
    
    // creating an object holding a const, using the factory method `create`:
    const MyClass const_holder  = create(const_param);
    
    // however you cannot prevent this:
    MyClass non_const_holder = create(const_param);
    
    // creating a const holder with a factory method
    // the type would *remember* that it is holding a const resource
    // and can act accordingly
    auto const_holder = create(const_param);
    
    // and you can also create a non const holder with the same factory
    auto non_const_holder = create(param);
    
    // (a) calling a non const method on the content of the non const holder
    non_const_holder->non_const_method();
    
    // (b) assigning a non const holder into a const holder object
    //     -- same as assigning char* to const char*
    const_holder = non_const_holder;
    
    // (a) calling a non const method on the content of a const holder
    const_holder->non_const_method(); // should be compilation error
    
    // (b) assigning a const holder into a non const holder object
    //     -- same as one cannot assign const char* to char*
    non_const_holder = const_holder; // should be compilation error
    
    template<class T>
    Pointer<T> create(T* t) {
        return Pointer<T>::create(t);
    }
    
    template<class T>
    class Pointer {
        T* ptr;
        Pointer(T* p) : ptr(p) {}
        friend class Pointer<const T>;
    public:
        // factory method
        static Pointer create(T* p) {
            return p;
        }
        operator T*() { return ptr; }
        operator const T*() const { return ptr; }
    };
    
    template<class T>
    class Pointer<const T> {
        const T* ptr;
        Pointer(const T* p) : ptr(p) {}
    public:
        Pointer(const Pointer<T>& other) {
            ptr = other.ptr;
        }
        // factory method
        static const Pointer create(const T* p) {
            return p; 
        }
        operator const T*() { return ptr; }
        operator const T*() const { return ptr; }
    };
    
    int main() {
        char str[] = "hello";
        const char* const_str = "hello";
        // non-const - good!
        auto s1 = create(str);
        // const holding non-const - ok!
        const auto s2 = create(str);
        // non-const that holds const - good!
        auto s3 = create(const_str);
        // also possible: const holding const
        const auto s4 = create(const_str);
    
        s1[4] = '!'; // OK
        // s2[0] = '#'; // obviously doesn't compile - s2 is const
        // s3[0] = '@'; // doesn't compile - which is good - s3 holds const!
        // s4[0] = 'E'; // obviously doesn't compile - s4 is const
        // avoids assignment of s3 that holds a const into s1 that holds a non-const
        // s1 = s3; // <= doesn't compile - good!
        s3 = s1; // allows assignment of `holding-non-const` into `holding-const`
        s3 = s2; // allows assignment of `holding-non-const` into `holding-const`
        s3 = s4; // allows assignment of `holding-const` into `holding-const`
    }
    
    #include <iostream>
    #include <cctype>
    #include <cassert>
    
    class MyClass
    {
      public:
        MyClass(char * str1, size_t size1) : str{str1}, size{size1}
        {
        }
        char * capitalizeFirstChar()
        {
            *str = std::toupper(*str);
            return str;
        }
        char nthChar(size_t n) const
        {
            assert(n < size);
            return str[n];
        }
    
        char * str;
        size_t size;
    };
    
    
    int main()
    {
        {
            static char str1[] = "abc";
            MyClass myclass1(str1, sizeof(str1) / sizeof(*str1));
    
            myclass1.capitalizeFirstChar();
            std::cout << myclass1.nthChar(0) << std::endl;
        }
        std::cout << "----------------------" << std::endl;
        {
            static const char str2[] = "abc";
            //             UGLY!!! const_cast
            const MyClass myclass2(const_cast<char *>(str2), sizeof(str2) / sizeof(*str2));
    
            // myclass2.capitalizeFirstChar();        // commented: will not compile
    
            std::cout << myclass2.nthChar(0) << std::endl;
    
            char c = 'x';
            // myclass2.str = &c;                     // commented: will not compile
                                                      // The const myclass2, does not
                                                      // allow modification of it's members
    
            myclass2.str[0] = 'Z';  // WILL COMPILE (!!) and should cause a segfault
                                    // The const myclass2, CANNOT PROTECT THE OBJECT POINTED TO by str
                                    // Reason: the member in MyClass is 
                                    //                      char *str  
                                    //         not 
                                    //                const char *str
    
            std::cout << myclass2.nthChar(0) << std::endl;
        }
    }
    
    #include <iostream>
    #include <cctype>
    #include <cassert>
    
    class MyClassConst
    {
      public:
        MyClassConst(const char * str1, size_t size1) : str{str1}, size{size1}
        {
        }
        char nthChar(size_t n) const
        {
            assert(n < size);
            return str[n];
        }
    
        const char * str;
        size_t size;
    };
    
    class MyClass : public MyClassConst
    {
      public:
        MyClass(char * str1, size_t size1) : MyClassConst{const_cast<const char *>(str1), size1}
        {
        }
        char * capitalizeFirstChar()
        {
            char * cp = const_cast<char *>(str);
            *cp       = std::toupper(*cp);
            return cp;
        }
    };
    
    
    int main()
    {
        {
            static char str1[] = "abc";
            MyClass myclass1(str1, sizeof(str1) / sizeof(*str1));
            myclass1.capitalizeFirstChar();
            std::cout << myclass1.nthChar(0) << std::endl;
        }
        std::cout << "----------------------" << std::endl;
        {
            static const char str2[] = "abc";
            //             NICE: no more const_cast
            const MyClassConst myclass2(str2, sizeof(str2) / sizeof(*str2));
    
            // a.capitalizeFirstChar();               // commented: will not compile
    
            std::cout << myclass2.nthChar(0) << std::endl;
    
            char c = 'x';
            // myclass2.str = &c;                     // commented: will not compile
                                                      // The const myclass2, does not
                                                      // allow modification of it's members
    
            // myclass2.str[0] = 'Z';                 // commented: will not compile
    
            std::cout << myclass2.nthChar(0) << std::endl;
        }
    }