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;
}
}