做外贸单网上都做的那些网站,南阳网站建设大旗电商,wordPress 要开放评论吗,南阳做玉器网站目录
构造函数
1.构造函数#xff1a;
2.构造函数的特点#xff1a;
默认构造函数 -- 没有参数的构造函数
1. 合成(自动)的默认构造函数(一般不常用)
1#xff09; 介绍#xff0c;以及为什么不使用 2#xff09;可以使用合成默认构造函数的情况
2. 自定义的默认…目录
构造函数
1.构造函数
2.构造函数的特点
默认构造函数 -- 没有参数的构造函数
1. 合成(自动)的默认构造函数(一般不常用)
1 介绍以及为什么不使用 2可以使用合成默认构造函数的情况
2. 自定义的默认构造函数 1介绍 有参构造函数 -- 带参数的构造函数 1. 自定义有参构造函数 1 简述 2) 使用有参构造函数实例化对象
3有参构造函数定义时需要注意
1. 使用的参数名字和类内的属性名字不同
2. 使用的构造函数参数的名字和类内的属性名字一样
3. 解决办法 使用this指针 指向当前对象(可以理解为调用函数的这个对象) 拷贝构造函数
1. 合成的拷贝构造函数(有很大风险) -- 浅拷贝
1简述 -- 浅拷贝 2合成拷贝构造函数具有很大的风险(浅拷贝的风险)
2. 自定义的拷贝构造函数 -- 深拷贝 1简述 -- 深拷贝
2使用深拷贝来解决浅拷贝的问题
3浅拷贝和深拷贝
4拷贝构造函数的调用时机
返回过程
5 拷贝构造函数注意事项
赋值构造函数
合成的赋值构造函数
1简述
2 代码
3合成赋值构造函数也存在问题 -- 也是浅拷贝
自定义的赋值构造函数
1含义 2代码
3赋值构造函数的写法
总结
构造函数
构造函数是c类中的概念,用来给对象的属性进行初始化和一些其它的操作(相当于创造一个东西时赋予它一些特性)。
1.构造函数
其实从字面意思来理解就是创造一个东西的函数对于类来说这个东西就是对象。
所以c语法规定在我们实例化一个类对象的时候都会默认调用构造函数。 2.构造函数的特点
1 创建对象时自己调用
2) 没有返回值函数名和类名一样
3 可以利用函数的重载写多个构造函数。 3. 注意
构造函数必须设置为公有,因为我们实例化对象在类外所以构造函数必须能够被外界使用。 默认构造函数 -- 没有参数的构造函数
1. 合成(自动)的默认构造函数(一般不常用)
1 介绍以及为什么不使用
我们上面说了c的语法规则实例化一个对象就会自动调用构造函数。
但是如果我们没有在类中写构造函数呢? 那么编译器会默认给我们生成一个合成的默认构造函数。这个构造函数其实内部什么也没有就是一个空函数。 那没什么要有它呢-- 也许为了语法统一。
但是使用合成的默认构造函数的风险很高。 看代码
class Human {
public:int getAge();void visit();private:int age;
};int Human::getAge() {return this-age;
}void Human::visit() {cout getAge() endl;
}int main(void) {Human man; // 创建man对象man.visit(); // 使用visit函数来打印age的值system(pause);return 0;
} 上面代码我们创建了一个对象并且我们没有在类中实现构造函数这是编译器会自己创建一个构造函数(空实现)。
我们通过visit()打印通过对外接口getAge()获得的age的值。-- 看上面的结果是一个很大的负值。这中情况很常见 -- 没有赋值直接使用。
为什么会是上面的结果呢
因为对象man中的属性age没有赋值在实例化的时候调用的也是编译器自己创建的默认构造函数也是空实现不会对age进行赋值所以age在使用的时候是没有赋值的。所以会出现问题。
这就是为什么不使用它的原因了。 2可以使用合成默认构造函数的情况
有一种情况我们是可以使用默认构造函数的。
class Human {
public:int getAge();void visit();private:int age 18; // 仅c11之后可以
};
比如代码中我们在类内定义的age变量,定义的时候已经初始化了。这时即使再输出age也不会出现上面的情况(构造函数不赋值的情况下)。 正常输出。
所以当我们类内的数据在定义的时候全都已经初始化了(必须全部初始化因为有不初始化的就存在风险)就可以使用合成默认构造函数了。
注意 只有c11之后才支持在类内定义数据时进行初始化。 2. 自定义的默认构造函数 1介绍
上面说到我们一般都不会使用合成默认构造函数我们一般都会自己定义一个构造函数。
在自定义的构造函数中我们就可以对相应的数据进行初始化了。 class Human {
public:int getAge();void visit();Human();private:int age;
};int Human::getAge() {return this-age;
}Human::Human() {age 18;
}void Human::visit() {cout getAge() endl;
}int main(void) {Human man; // 创建man对象man.visit(); // 使用visit函数来打印age的值system(pause);return 0;
}
上面代码就定义了一个默认构造函数Human()我们在实现构造函数中将age属性进行了初始化。
在自定义的构造函数中我们可以根据自己的需要进行设置将相应的属性进行初始化。
注意 当我们自己定义了构造函数的时候编译器就不会给我们提供了。 有参构造函数 -- 带参数的构造函数 1. 自定义有参构造函数 1 简述
前面提到构造函数可以有多个带参数的构造函数是我们自己写的。 前面写的默认构造函数没有参数的。
有参构造函数就是带参数的构造函数所以它的书写规则和无参构造很类似就是带了个参数。
class Human {
public:Human(int age, string name);int getAge();string getName();void visit();
private:int age;string name;
};int Human::getAge() {return this-age;
}string Human::getName() {return name;
}void Human::visit(){cout getAge() endl;cout getName() endl;
}Human::Human(int age, string name) {this-age age; // 如果名字相同使用this指针或者可以将参数的名字设置成不一样的this-name name;
}int main(void) {Human man1(18, 男神);man1.visit();system(pause);return 0;
}
代码中定义了一个有参构造函数Human(int age,string name) ; 2) 使用有参构造函数实例化对象
代码中有参构造有两个参数用来接收用户用来初始化属性的值。
所以在使用有参构造函数创建对象的时候需要传入参数。Human man1(18,男神); 就是调用有参构造函数进行创建对象。
3有参构造函数定义时需要注意
1. 使用的参数名字和类内的属性名字不同
class Human {
public:// 有参构造函数Human(int age1, string name1);
private:int age;string name;
};Human::Human(int age1, string name1) {age age1;name name1;
}
当有参构造函数的参数名字和类内属性名,名字不一样的时候可以直接进行赋值。
2. 使用的构造函数参数的名字和类内的属性名字一样
class Human {
public:// 有参构造函数Human(int age, string name);
private:int age;string name;
};Human::Human(int age, string name) {age age; // 此时这两个age都表示的是参数所以不正确。name类似name name;
}
当参数的名字和属性名字相同的时候就不能直接赋值了。
age age; // 此时这两个age都表示的是形参里面的age并不是类内属性的age所以不对。
3. 解决办法 使用this指针 指向当前对象(可以理解为调用函数的这个对象)
Human::Human(int age, string name) {this-age age; this-name name;
}
使用this之后就可以了。 因为this指向当前对象所以this-age就是指当前对象的age属性。 拷贝构造函数
拷贝构造函数在一个对象初始化的时候给它赋值另外一个对象这是就会默认调用拷贝构造函数。
Human man2 man1;
1. 合成的拷贝构造函数(有很大风险) -- 浅拷贝
1简述 -- 浅拷贝
合成的拷贝构造函数就是当自己没有实现拷贝构造函数的时候编译器会自动生成一个合成拷贝构造函数。 拷贝构造函数会将man1中属性的值拷贝到man2的属性中。 2合成拷贝构造函数具有很大的风险(浅拷贝的风险) 浅拷贝就是只将属性的值拷贝到另外一个对象。 这样看浅拷贝好像没有什么问题。如果使用普通的变量没有什么问题,但是如果属性中指针那么就会出现问题。
举个例子
int age;
string name;
char *a;man1 : age12,name帅哥,*a aHuman man2 man1;
将man1中的值拷贝到man2中 我们没有定义拷贝构造函数所以编译器自动生成进行浅拷贝 将man1 中 age 的值复制给 man2中 agename也同理
重点来看指针的拷贝浅拷贝只是将指针变量a中存放的地址拷贝到man2的a中也就是说进行浅拷贝之后man1中的a和man2中的a指向同一片内存同一块数据。
可能会疑问问什么不能指向同一个呢 -- 很简单man1 和 man2两个对象毫不相关如果这个指针用来表示的是资产那你说两个人的资产怎么能放到一起呢。
所以这样用一定会出问题会有很大的风险。
2. 自定义的拷贝构造函数 -- 深拷贝 1简述 -- 深拷贝
前面说到系统自动提供的拷贝构造函数只能进行浅拷贝会造成很大的风险。
那怎么办呢-- 那就自己实现一个呗然后使用深拷贝来解决这个问题
2使用深拷贝来解决浅拷贝的问题 浅拷贝的问题是 对指针进行拷贝时不会开辟新空间直接将新对象的指针指向别的对象的内存这就会造成很大的风险。
问那深拷贝如何解决这个问题呢
我们可以在自己实现的拷贝构造函数中给新对象的指针开辟一片空间来存放其他对象指针中的值 -- 这样就不会共用同一片空间了。
class Human {
public:Human();Human(const Human); // 函数声明可以不写参数名称
private:int age;string name;char* f1;
};Human::Human() {age 20;name 帅哥;f1 new char[10];strcpy_s(f1,10,好好学习); // 使用字符串拷贝给字符串进行赋值
}Human::Human(const Human man) {age man.age;name man.name;f1 new char[10];strcpy_s(f1, 10, man.f1); // 将man中f1指向内存的值拷贝到此对象的f1中
}int main(void) {Human man1;Human man2 man1; // 在对象初始化时赋值给另外一个对象则会默认调用拷贝构造函数system(pause);return 0;
}
Human(const Human); 就是我们自定义的拷贝构造函数声明。
在函数实现的过程中 我们又为新对象的f1指针创建了一片内存用来存放其它对象的值。
3浅拷贝和深拷贝
浅拷贝系统自己生成的拷贝构造函数只是将变量中的值浅浅的复制过去不管你是不是有风险 比如对于指针变量只是将指针变量存放的地址复制过去使它指向了同一片空间。
深拷贝 为了解决浅拷贝带来的风险我们需要自己实现拷贝构造函数为新对象的指针开辟空间将别的对象指向的内容复制过去而不是简单的将指针中存储的地址复制过去。
4拷贝构造函数的调用时机
1. 当函数参数不是引用(值传递)参数类型是我们定义的类的时候
void test(Human man) { // 传参数的过程就是 Human man man1 这不就是拷贝构造// 测试语句
}int main(void) {Human man1;test(man1);system(pause);return 0;
}
2. 返回值为我们定义的类
Human test(Human man) {// 测试语句return man;
}int main(void) {Human man1;test(man1);system(pause);return 0;
}
返回过程
其实就是创建一片临时空间来存放man的值然后返回给主调函数。 这个过程其实和Human man1 man;是类似的只是此处我们不知道变量名称。
3. 初始化对象时直接使用另外一个对象初始化使用或者()都可以。都会自动调用拷贝构造函数。
int main(void) {Human man1;Human man2 man1; // 在对象初始化时赋值给另外一个对象则会默认调用拷贝构造函数Human man3(man1);system(pause);return 0;
}
4. 使用对象数组初始化的时候
int main(void) {Human man1;Human man2 man1; Human man3(man1);Human man4[3] { man1,man2,man3};system(pause);return 0;
}
其实就是定义了一个数组数组中的每个元素都是Human类型所以其实就相当于
Human man4[0] man1; Human man4[1] man2; Human man4[2] man3; 自动调用拷贝构造函数。
5 拷贝构造函数注意事项 1. 拷贝构造函数的参数类型必须是 const Human man 这种 2. 拷贝构造函数的参数只能有一个因为初始化的时候只能一个对象作为右值。 3. 由于2.所有拷贝构造函数只能有一个,因为它无法进行函数重载因为它只能有一个参数并且参数的类型和数量都是固定的。 (有参构造可以重载因为参数个数个类型不受限制) 赋值构造函数
合成的赋值构造函数
1简述
赋值构造函数和其他的构造函数不同(具体看实现)。 合成赋值构造函数也是系统自己提供
2 代码
int main(void) {Human man1,man2;man2 man1; // 在不初始化时,进行赋值调用赋值构造函数system(pause);return 0;
}
当我们使用将一个对象赋值给另外一个对象的时候系统会自动调用赋值构造函数。(注意不是初始化的时候初始化时调用的是拷贝构造函数)
3合成赋值构造函数也存在问题 -- 也是浅拷贝
还是浅拷贝当属性中有指针时虽然两个对象的指针都指向各自的内存,但是合成赋值构造函数只是进行简单的赋值对指针进行赋值时,只是将指针变量的值赋值给新对象的指针这样又会导致两个对象的指针直系那个同一片内存。(浅拷贝)
自定义的赋值构造函数
1含义
和上面一样既然浅拷贝有问题,那么我们就自定义一个赋值构造函数来实现深拷贝。 2代码
class Human {
public:Human();Human operator(const Human man); // 赋值构造函数 -- 运算符重载
private:int age;string name;char* f1;
};Human::Human() {age 20;name 帅哥;f1 new char[10];strcpy_s(f1,10,好好学习); // 使用字符串拷贝给字符串进行赋值
}Human Human::operator(const Human man) {age man.age;name man.name;// 深拷贝strcpy_s(f1, 10, man.f1);return *this; // 返回此对象
}int main(void) {Human man1,man2;man2 man1; // 在不初始化时,进行赋值调用赋值构造函数system(pause);return 0;
}
问为什么此处的深拷贝不需要开辟新空间
此处的深拷贝已经不需要开辟空间了,因为我们在实例化对象的时候 已经在默认构造函数中对每个对象的f1指针都开辟了单独的空间。所以不需要再开辟空间了。
我们只需要将man1中的f1内存中的值拷贝到man2的f1指向的内存即可。(而不是将f1中的地址赋值过去)。
3赋值构造函数的写法 1. 首先赋值构造函数就是一个的重载函数 2. 参数传入的是作为右值的对象而调用函数的是左值的对象。(this指针指向当前对象 ) 3. 返回Human, 是用来返回调用函数的对象的。this指向调用函数的当前对象*this就是此对象。 4. 3.的原因 为了能够实现连续赋值的情况 -- man man1 man2; man1 ma2; 调用赋值构造函数,返回man1, 再进行 man man1。 总结
综合上面的看构造函数其实就是当我们对对象进行相应的操作时,系统会自动去调用的函数。为了构造当前对象去调用函数。