C++不同的构造函数调用
我写了一个小类来学习不同的构造函数调用C++不同的构造函数调用,c++,constructor,C++,Constructor,我写了一个小类来学习不同的构造函数调用 #include <iostream> #include <cstdlib> #include <cstring> class String { private: char *str; public: explicit String(const char *p); String(String&& StringObject);
#include <iostream>
#include <cstdlib>
#include <cstring>
class String {
private:
char *str;
public:
explicit String(const char *p);
String(String&& StringObject);
String(String& stringObject);
~String();
friend std::ostream& operator<<(std::ostream& os, const String& s);
friend String operator+(const String& s1, const String& s2);
};
String::String(const char *p)
{
size_t l = strlen(p) + 1;
str = (char *)malloc(sizeof(char) * l);
if (str)
strcpy(str, p);
std::cout << "constructor call" << std::endl;
}
String::String(String& stringObject)
{
str = (char *)malloc(sizeof(char) * (strlen(stringObject.str) + 1));
strcpy(str, stringObject.str);
std::cout << "copy constructor call" << std::endl;
}
String::~String()
{
free(str);
}
String::String(String&& stringObject)
{
this->str = stringObject.str;
stringObject.str = nullptr;
std::cout << "move constructor call" << std::endl;
}
std::ostream& operator<<(std::ostream& os, const String& s)
{
return os << s.str;
}
String operator+(const String& s1, const String& s2)
{
size_t sl1 = strlen(s1.str);
size_t sl2 = strlen(s2.str);
char str[sl1 + sl2 + 1];
strcpy(str, s1.str);
strcpy(str+sl1, s2.str);
return String{str};
}
String doNothing(String obj)
{
std::cout << "output in function: " << obj << std::endl;
return obj;
}
int main()
{
String s1("text");
String s2("and more text");
std::cout << "output: " << s1 << std::endl;
String s3 = doNothing(s1+ String{" "} + s2);
String s4 = doNothing(s1);
String s5 = s1 + s4;
}
我认为第4行到第6行的构造函数调用来自方法调用
String s3 = doNothing(s1+ String{" "} + s2);
为什么方法调用不像第二个方法调用那样导致对复制构造函数的调用
String s4 = doNothing(s1);
也许是因为s1是左值
只有当函数返回一个对象时,或者当返回一个对象的引用或指针时,才能调用move构造函数吗?我假设从1开始计算行数- 带有复杂表达式的情况 我们希望下面的语句在principe中为String{}构造一个临时字符串,然后为两个+中的每一个构造一个临时字符串。由于参数obj将从临时对象构造,因此可以使用move构造函数:
String s3 = doNothing(s1+ String{" "} + s2);
移动构造函数的思想是能够利用原始对象是一次性的这一事实
函数的返回值也是一个临时值,这个值用于使用move构造函数构造s3
但是我们不应该有3个构造器和2个移动构造器吗?不,因为有。这会使编译器避免不必要的复制/移动,并直接构造到目标中。对于参数,这里发生这种情况
左值的情况
下一条语句使用左值s1的复制构造函数构造参数obj:
这并不令人惊讶。原则上,该值会复制到临时返回值,然后使用该值将构造s4从临时返回值移动
但是,复制省略将其简化为复制构造函数和移动
分析发生了什么
通过显示对象的地址,可以更详细地分析发生了什么。这有助于理解在什么对象上发生的情况:
class String {...};
String::String(const char *p)
{
size_t l = strlen(p) + 1;
str = new char[l];
if (str)
strcpy(str, p);
std::cout << "constructor call:" << this<<"="<<str<< std::endl;
}
String::String(const String& stringObject)
{
str = new char[ strlen(stringObject.str) + 1];
strcpy(str, stringObject.str);
std::cout << "copy constructor call:" <<this<<"="<<str<< std::endl;
}
String::~String()
{
delete[] str;
}
String::String(String&& stringObject)
{
this->str = stringObject.str;
stringObject.str = nullptr;
std::cout << "move constructor call:"<<this <<"="<<str<< std::endl;
}
String operator+(const String& s1, const String& s2)
{
size_t sl1 = strlen(s1.str);
size_t sl2 = strlen(s2.str);
char str[sl1 + sl2 + 1];
strcpy(str, s1.str);
strcpy(str+sl1, s2.str);
return String{str};
}
String doNothing(String obj)
{
std::cout << "output in function: " <<&obj<<":"<< obj << std::endl;
return obj;
}
无关建议
<>我强烈建议你去掉Malc和C++中的免费,所以我在这里使用NeX[]和Dele[]或新的,如果不是数组的话,删除。在第二步中,您还可以去掉strcpy,我假设您从1开始计算行数- 带有复杂表达式的情况 我们希望下面的语句在principe中为String{}构造一个临时字符串,然后为两个+中的每一个构造一个临时字符串。由于参数obj将从临时对象构造,因此可以使用move构造函数:
String s3 = doNothing(s1+ String{" "} + s2);
移动构造函数的思想是能够利用原始对象是一次性的这一事实
函数的返回值也是一个临时值,这个值用于使用move构造函数构造s3
但是我们不应该有3个构造器和2个移动构造器吗?不,因为有。这会使编译器避免不必要的复制/移动,并直接构造到目标中。对于参数,这里发生这种情况
左值的情况
下一条语句使用左值s1的复制构造函数构造参数obj:
这并不令人惊讶。原则上,该值会复制到临时返回值,然后使用该值将构造s4从临时返回值移动
但是,复制省略将其简化为复制构造函数和移动
分析发生了什么
通过显示对象的地址,可以更详细地分析发生了什么。这有助于理解在什么对象上发生的情况:
class String {...};
String::String(const char *p)
{
size_t l = strlen(p) + 1;
str = new char[l];
if (str)
strcpy(str, p);
std::cout << "constructor call:" << this<<"="<<str<< std::endl;
}
String::String(const String& stringObject)
{
str = new char[ strlen(stringObject.str) + 1];
strcpy(str, stringObject.str);
std::cout << "copy constructor call:" <<this<<"="<<str<< std::endl;
}
String::~String()
{
delete[] str;
}
String::String(String&& stringObject)
{
this->str = stringObject.str;
stringObject.str = nullptr;
std::cout << "move constructor call:"<<this <<"="<<str<< std::endl;
}
String operator+(const String& s1, const String& s2)
{
size_t sl1 = strlen(s1.str);
size_t sl2 = strlen(s2.str);
char str[sl1 + sl2 + 1];
strcpy(str, s1.str);
strcpy(str+sl1, s2.str);
return String{str};
}
String doNothing(String obj)
{
std::cout << "output in function: " <<&obj<<":"<< obj << std::endl;
return obj;
}
无关建议
<>我强烈建议你去掉Malc和C++中的免费,所以我在这里使用NeX[]和Dele[]或新的,如果不是数组的话,删除。在第二步中,您还可以去掉strcpy表达式s1+String{}+s2将在调用doNothing之前构造字符串实例。表达式s1+String{}+s2将在调用doNothing之前构造字符串实例。我通常使用char数组而不是malloc,但在本例中不是。为什么?我不知道。我会记住的!我可以用什么来代替strcpy?你是指std::string还是strncpy?@MichaelSchäfer我的意思是这是一种适用于所有类型容器的通用算法。由于它是基于长度或上界的,所以比strcpyI使用字符数组而不是malloc的风险更小,但在本例中不是这样。为什么?我不知道。我会记住的!我可以用什么来代替strcpy?你是指std::string还是strncpy?@MichaelSchäfer我的意思是这是一种适用于所有类型容器的通用算法。由于它是基于长度或上限的,因此风险比strcpy要小