今天的知识内容:
1)为什么会出现构造函数与析构函数
构造函数3种初始化
2)拷贝函数4种应用时机
【新发现】在拷贝构造函数里面 可以直接访问私有成员属性
两个已经初始化完毕的对象 用= 号赋值不会调用构造函数
3)匿名对象的去与留
GCC 与 VS环境编译的差异
一旦自定义了构造函数,你必须去调用
4)【强化】构造函数 与 析构函数的执行顺序强化练习
对象中包含有其他对象时,先初始化别人,再初始化自己
对象中包含有其他对象时,先析构自己,再析构别人
默认构造函数是浅拷贝
5)拷贝构造函数的深拷贝与浅拷贝
【注意】两个已经初始化完毕的对象 用= 号赋值不会调用构造函数,但是会调用拷贝构造函数
6)构造函数初始化列表
类中含有const属性变量,必须在构造函数的初始化列表赋值
7)匿名对象的生命周期,被接收 OR 无接收
8)在构造函数中调用构造函数,是一种危险的行为
9)C++ new delete 基础数据类型,数组 ,对象
new delete 与 malloc free 互用
10)类中静态变量,所有对象公用
类中静态变量,静态函数基本调用
类中静态函数只能调用静态变量
11)面向对象模型初探--其实还是C语言的面向过程
12)this指针初步
今天作业
为什么会出现构造函数 与 析构函数
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
void init()
{
a = 1;
b = 2;
}
private:
int a;
int b;
};
int main()
{
Test arr[3] ;
arr[0].init(); //显式的执行初始化
arr[1].init();
arr[2].init();
Test haha[19999] ; //请问这个怎么去初始化
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
类的构造函数 与 析构函数
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
using namespace std;
class Test
{
public:
Test()
{
cout << "构造函数 \n";
}
~Test()
{
cout << "析构函数 \n";
}
};
int main()
{
Test t1;
Test t2;
cout << "Hello World \n";
return 0;
}
chunli@Linux:~/c++$ g++ -g -Wall main.cpp && ./a.out
构造函数
构造函数
Hello World
析构函数
析构函数
构造函数初始化变量:
析构函数释放内存
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
Test()
{
a = 10;
p = (char *)malloc(100);
strcpy(p,"Hello Linux!");
cout << "构造函数 \n";
}
void printf_var()
{
cout << a << endl;
cout << p << endl;
}
~Test()
{
if(p != NULL)
{
free(p);
}
cout << "析构函数 \n";
}
private:
char *p;
int a;
};
void fun()
{
Test t1;
Test t2;
t1.printf_var();
}
int main()
{
fun();
cout << "-------------\n";
return 0;
}
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
构造函数
构造函数
10
Hello Linux!
析构函数
析构函数
-------------
chunli@Linux:~/c++$
类的构造函数3种初始化方式:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
Test()
{
cout << "没有默认参数的构造函数 \n";
}
Test(int _a)
{
a = _a;
cout << "a=" << a <<"带有1个默认参数的构造函数 \n";
}
Test(int _a,int _b)
{
a = _a;
b = _b;
cout << "a=" << a << " b=" << b<< "带有2个默认参数的构造函数 \n";
}
Test(const Test &OBJ) //用一个对象初始化另一个对象
{
cout << "赋值构造函数构造函数 \n";
}
~Test()
{
cout << "析构函数 \n";
}
private:
int a;
int b;
};
void fun()
{
Test t1; //调用无参数的构造函数
//===== 对象的初始化 ==========
/*1*/Test t2(1,2);
//逗号表达式的最后一个值是整个表达式的值 g++ -Wall 编译有警告:逗号操作符的左操作数没有效果
/*2*/Test t3 = (3,4,5,6,7); //这个等号C++编译器做了增强
/*3*/Test t4 = Test(1,4);/*匿名对象*/
t1 = t4; //对象的赋值,这个等号是普通的赋值操作
Test t5(t2);
}
int main()
{
fun();
cout << "-------------\n";
return 0;
}
正常编译:
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
没有默认参数的构造函数
a=1 b=2带有2个默认参数的构造函数
a=7带有1个默认参数的构造函数
a=1 b=4带有2个默认参数的构造函数
赋值构造函数构造函数
析构函数
析构函数
析构函数
析构函数
析构函数
-------------
g++ -Wall编译
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
main.cpp: In function ‘void fun()’:
main.cpp:44:20: warning: left operand of comma operator has no effect [-Wunused-value]
/*2*/Test t3 = (3,4,5,6,7);
^
没有默认参数的构造函数
a=1 b=2带有2个默认参数的构造函数
a=7带有1个默认参数的构造函数
a=1 b=4带有2个默认参数的构造函数
赋值构造函数构造函数
析构函数
析构函数
析构函数
析构函数
析构函数
-------------
chunli@Linux:~/c++$
【拷贝构造函数应用的4种时机】
1,抛题 -- 乱码的出现:
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
Test(int _a,int _b)
{
cout << "2个参数\n";
a = _a;
b = _b;
}
Test(Test &OBJ)
{
cout << "赋值构造函数被调用 \n";
}
void fun1()
{
cout << "a="<< a << " ";
cout << "b="<< b << " \n";
}
private:
int a;
int b;
};
int main()
{
Test t1(1,5); t1.fun1();
Test t2 = t1;
//用t1 的参数初始化t2.会调用赋值构造函数,
//如果没有编译器自己造,如果自定义了赋值构造函数,就直接调用
t2.fun1(); //此时 输出属性值是乱码。因为我们自定义的构造函数什么也没有做
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
2个参数
a=1 b=5
拷贝构造函数被调用
a=-336889536 b=32764
拷贝构造函数的使用:
第1种调用方法:Test t2 = t1;
chunli@Linux:~/c++$ cat main.cpp
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
Test(int _a,int _b)
{
cout << "2个参数\n";
a = _a;
b = _b;
}
Test(const Test &OBJ)
{
a = OBJ.a + 100;
b = OBJ.a + 150;
cout << "拷贝构造函数被调用 \n";
}
void fun1()
{
cout << "a="<< a << " ";
cout << "b="<< b << " \n";
}
private:
int a;
int b;
};
int main()
{
Test t1(1,5); t1.fun1();
Test t2 = t1;
t2.fun1();
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
2个参数
a=1 b=5
拷贝构造函数被调用
a=101 b=151
拷贝构造函数的第2 中调用方式:Test t2(t1);
chunli@Linux:~/c++$ cat main.cpp
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
Test(int _a,int _b)
{
cout << "2个参数\n";
a = _a;
b = _b;
}
Test(const Test &OBJ)
{
a = OBJ.a + 100;
b = OBJ.a + 150;
cout << "拷贝构造函数被调用 \n";
}
void fun1()
{
cout << "a="<< a << " ";
cout << "b="<< b << " \n";
}
private:
int a;
int b;
};
int main()
{
Test t1(1,5); t1.fun1();
Test t2(t1); t2.fun1();
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
2个参数
a=1 b=5
拷贝构造函数被调用
a=101 b=151
【答疑】对象的赋值操作t2 = t1 ,不会调用赋值构造函数:
chunli@Linux:~/c++$ cat main.cpp
chunli@Linux:~/c++$ cat main.cpp
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
Test(int _a,int _b)
{
cout << "2个参数\n";
a = _a;
b = _b;
}
Test(const Test &OBJ)
{
a = OBJ.a + 100;
b = OBJ.a + 150;
cout << "赋值构造函数被调用 \n";
}
void fun1()
{
cout << "a="<< a << " ";
cout << "b="<< b << " \n";
}
private:
int a;
int b;
};
int main()
{
Test t1(1,5); t1.fun1();
Test t2(99,99); t2.fun1();
t2 = t1; t2.fun1();
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
2个参数
a=1 b=5
2个参数
a=99 b=99
a=1 b=5
赋值构造函数的第3中应用:函数
chunli@Linux:~/c++$ cat main.cpp
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
Test(int _a,int _b)
{
cout << "2个参数\n";
a = _a;
b = _b;
}
Test(const Test &OBJ)
{
a = OBJ.a + 100;
b = OBJ.a + 150;
cout << "赋值构造函数被调用 \n";
}
~Test()
{
cout << "a=" << a <<" b =" << b<< "析构函数被调用\n";
}
void fun1()
{
cout << "a="<< a << " ";
cout << "b="<< b << " \n";
}
private:
int a;
int b;
};
void fun2(Test OBJ) //会自动调用类的赋值构造函数
{
OBJ.fun1();
}
void fun1()
{
Test t1(1,5); t1.fun1();
Test t2 = t1; t2.fun1();
cout << "t2已经完成初始化\n";
fun2(t2); //t2实参会初始化形参OBJ,会调用赋值构造函数
}
int main()
{
fun1();
return 0;
}
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
2个参数
a=1 b=5
赋值构造函数被调用
a=101 b=151
t2已经完成初始化
赋值构造函数被调用
a=201 b=251
a=201 b =251析构函数被调用
a=101 b =151析构函数被调用
a=1 b =5析构函数被调用
【难点】赋值构造函数应用4
【匿名对象的去和留】-- 去
1,没有对象接收函数的返回
在VS编译下,返回的对象会调用一次析构函数,然后没人接收就析构掉
在GCC下,不会出现这种情况
【这第4种方法 VS环境 与 GCC 表现不一样】
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdio.h>
using namespace std;
class Test
{
public:
Test(int _a, int _b)
{
a = _a;
b = _b;
cout << "a=" << a << " b =" << b << "构造函数初始化, 有2个参数\n";
}
Test(const Test &OBJ)
{
a = OBJ.a + 1;
b = OBJ.b + 1;
cout << "a=" << a << " b =" << b << "赋值构造函数被调用 \n";
}
~Test()
{
cout << "a=" << a << " b =" << b << "析构函数被调用\n";
}
void fun1()
{
cout << "a=" << a << " ";
cout << "b=" << b << " \n";
}
private:
int a;
int b;
};
//函数返回一个Test 对象 (复杂类型的)
//返回的是一个新的匿名对象
//所以会调用匿名对象类的copy构造函数
Test fun1()
{
Test A(66, 77);
return A;
//新的对象作为匿名对象返回
}
//用这个函数观测对象的生命周期
void fun2()
{
//微软VS环境有效:用b对象b来接返回的匿名对象,不会直接析构
//GCC 既不执行copy构造函数,也不执行析构函数
fun1();
}
int main()
{
fun2();
getchar();
return 0;
}
GCC编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
a=66 b =77构造函数初始化, 有2个参数
a=66 b =77析构函数被调用
VS编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
a=66 b =77构造函数初始化, 有2个参数
a=67 b =78赋值构造函数被调用
a=66 b =77析构函数被调用
a=67 b =78析构函数被调用
【难点】赋值构造函数应用4
【匿名对象的去和留】-- 留
1,创建一个对象来接收函数的返回
在VS编译下,返回的对象会调用一次析构函数,然后被接收就不会直接析构掉
在GCC下,不会出现这种情况
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdio.h>
using namespace std;
class Test
{
public:
Test(int _a, int _b)
{
a = _a;
b = _b;
cout << "a=" << a << " b =" << b << "构造函数初始化, 有2个参数\n";
}
Test(const Test &OBJ)
{
a = OBJ.a + 1;
b = OBJ.b + 1;
cout << "a=" << a << " b =" << b << "赋值构造函数被调用 \n";
}
~Test()
{
cout << "a=" << a << " b =" << b << "析构函数被调用\n";
}
void fun1()
{
cout << "a=" << a << " ";
cout << "b=" << b << " \n";
}
private:
int a;
int b;
};
//函数返回一个Test 对象 (复杂类型的)
//返回的是一个新的匿名对象
//所以会调用匿名对象类的copy构造函数
Test fun1()
{
Test A(66, 77);
return A;
//新的对象作为匿名对象返回
}
//用这个函数观测对象的生命周期
void fun2()
{
//微软VS环境有效:用b对象b来接返回的匿名对象,不会直接析构
//GCC 既不执行copy构造函数,也不执行析构函数
//注意此时是一个新的对象,VS会调用copy构造函数,不会析构
Test b = fun1();
b.fun1();
}
int main()
{
fun2();
getchar();
return 0;
}
GCC下编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
a=66 b =77构造函数初始化, 有2个参数
a=66 b=77
a=66 b =77析构函数被调用
VS下编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
a=66 b =77构造函数初始化, 有2个参数
a=67 b =78赋值构造函数被调用
a=66 b =77析构函数被调用
a=67 b=78
a=67 b =78析构函数被调用
【难点】赋值构造函数应用4
【匿名对象的去和留】-- 去
1,创建一个对象来接收函数的返回
在VS编译下,返回的对象会调用一次析构函数,然后被接收就不会直接析构掉
在GCC下,不会出现这种情况
chunli@Linux:~/c++$ cat main.cpp
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdio.h>
using namespace std;
class Test
{
public:
Test(int _a, int _b)
{
a = _a;
b = _b;
cout << "a=" << a << " b =" << b << "构造函数初始化, 有2个参数\n";
}
Test(const Test &OBJ)
{
a = OBJ.a + 1;
b = OBJ.b + 1;
cout << "a=" << a << " b =" << b << "赋值构造函数被调用 \n";
}
~Test()
{
cout << "a=" << a << " b =" << b << "析构函数被调用\n";
}
void fun1()
{
cout << "执行对象的成员函数 ";
cout << "a=" << a << " ";
cout << "b=" << b << " \n";
}
private:
int a;
int b;
};
//函数返回一个Test 对象 (复杂类型的)
//返回的是一个新的匿名对象
//所以会调用匿名对象类的copy构造函数
Test fun1()
{
cout << "fun1 start \n" ;
//A 是局部变量,会被析构
Test A(66, 77);
return A;
//新的对象作为匿名对象返回
cout << "fun1 end \n" ;
}
//用这个函数观测对象的生命周期
void fun2()
{
//微软VS环境有效:用b对象b来接返回的匿名对象,不会直接析构
//GCC 既不执行copy构造函数,也不执行析构函数
cout << "fun2 start \n" ;
Test b(1,2);
b.fun1();
cout << "因为此时匿名对象并没有转换成新对象,匿名对象就会析构\n";
b = fun1();
b.fun1();
cout << "fun2 end \n" ;
}
int main()
{
fun2();
getchar();
return 0;
}
chunli@Linux:~/c++$
GCC编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
fun2 start
a=1 b =2构造函数初始化, 有2个参数
执行对象的成员函数 a=1 b=2
因为此时匿名对象并没有转换成新对象,匿名对象就会析构
fun1 start
a=66 b =77构造函数初始化, 有2个参数
a=66 b =77析构函数被调用
执行对象的成员函数 a=66 b=77
fun2 end
a=66 b =77析构函数被调用
chunli@Linux:~/c++$
VS编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
fun2 start
a=1 b =2构造函数初始化, 有2个参数
执行对象的成员函数 a=1 b=2
因为此时匿名对象并没有转换成新对象,匿名对象就会析构
fun1 start
a=66 b =77构造函数初始化, 有2个参数
a=67 b =78赋值构造函数被调用
a=66 b =77析构函数被调用
a=67 b =78析构函数被调用
执行对象的成员函数 a=67 b=78
fun2 end
a=67 b =78析构函数被调用
结论: 有关 匿名对象的去和留【VS环境】
如果用匿名对象 初始化 另外一个同类型的对象, 匿名对象 转成有名对象
如果用匿名对象 赋值给 另外一个同类型的对象, 匿名对象 被析构
#include <iostream>
using namespace std;
class Location
{
public:
Location( int xx = 0 , int yy = 0 )
{
X = xx ; Y = yy ; cout << "Constructor Object.\n" ;
}
//copy构造函数 完成对象的初始化
Location(const Location & obj) //copy构造函数
{
X = obj.X; Y = obj.Y;
}
~Location()
{
cout << X << "," << Y << " Object destroyed." << endl ;
}
int GetX () { return X ; } int GetY () { return Y ; }
private : int X , Y ;
} ;
//g函数 返回一个元素
//结论1 : 函数的返回值是一个元素 (复杂类型的), 返回的是一个新的匿名对象(所以会调用匿名对象类的copy构造函数)
//
//结论2: 有关 匿名对象的去和留
//如果用匿名对象 初始化 另外一个同类型的对象, 匿名对象 转成有名对象
//如果用匿名对象 赋值给 另外一个同类型的对象, 匿名对象 被析构
//
//你这么写代码,设计编译器的大牛们:
//我就给你返回一个新对象(没有名字 匿名对象)
Location g()
{
Location A(1, 2);
return A;
}
//
void objplay2()
{
g();
}
//
void objplay3()
{
//用匿名对象初始化m 此时c++编译器 直接把匿名对转成m;(扶正) 从匿名转成有名字了m
Location m = g();
printf("匿名对象,被扶正,不会析构掉\n");
cout<<m.GetX()<<endl;;
}
void objplay4()
{
//用匿名对象 赋值给 m2后, 匿名对象被析构
Location m2(1, 2);
m2 = g();
printf("因为用匿名对象=给m2, 匿名对象,被析构\n");
cout<<m2.GetX()<<endl;;
}
void main()
{
//objplay2();
//objplay3();
objplay4();
cout<<"hello..."<<endl;
system("pause");
return ;
}
【回顾上午】
构造函数初始化的三种方式:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class Test
{
public:
Test(int _a)
{
a = _a;
cout << "a=" << a << "构造函数初始化, 有1个参数\n";
}
Test(int _a,int _b)
{
a = _a;
b = _b;
cout << "a=" << a << " b= " << b << "构造函数初始化, 有2个参数\n";
}
void fun()
{
cout << "执行对象的成员函数 ";
cout << "a=" << a << " ";
cout << "b=" << b << " \n";
}
private:
int a;
int b;
};
int main()
{
Test t1(1,2); t1.fun();//C++编译器自动调用构造函数
Test t2 = (1,5,6); t2.fun();//C++编译器自动调用构造函数
Test t3 = Test(4,5); //程序员手动调用构造函数
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
a=1 b= 2构造函数初始化, 有2个参数
执行对象的成员函数 a=1 b=2
a=6构造函数初始化, 有1个参数
执行对象的成员函数 a=6 b=0
a=4 b= 5构造函数初始化, 有2个参数
【回顾】拷贝构造函数的4种使用时机
GCC编译器 与 VS编译有些区别:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
Test(int _a)
{
a = _a;
cout << "a=" << a << "构造函数初始化, 有1个参数\n";
}
Test(int _a,int _b)
{
a = _a;
b = _b;
cout << "a=" << a << " b=" << b << "构造函数初始化, 有2个参数\n";
}
Test(const Test &OBJ)
{
a = OBJ.a + 1;
b = OBJ.b + 1;
num++;
cout << "第"<< num <<"次调用拷贝构造函数";
cout << "a=" << a << " b=" << b <<endl ;
}
~Test()
{
cout << "a=" << a << " b=" << b << "析构函数被调用\n";
}
void fun()
{
cout << "执行对象的成员函数 ";
cout << "a=" << a << " ";
cout << "b=" << b << " \n";
}
private:
int a;
int b;
};
void fun1()
{
cout << "拷贝函数的调用时机,第1种和第2种 \n";
Test t1(1,2); t1.fun();
Test t2 = t1; t2.fun(); //C++编译器会调用拷贝构造函数
Test t3(t2) ; t3.fun(); //C++编译器会调用拷贝构造函数
}
void fun2(Test OBJ)
{
cout << "拷贝函数的调用时机,第3种\n";
}
Test fun3() //返回一个Test类
{
cout << "拷贝函数的调用时机,第4种\n";
Test t1(7,8);
return t1;
}
int main()
{
fun1(); //拷贝函数的调用时机,第1种和第2种
Test t1(4,5); fun2(t1); //拷贝函数的调用时机,第3种
Test t2 = fun3(); //弄了一个新的对象接受了匿名对象,这个不会直接执行析构函数
Test t3(11,12); t3 = fun3(); //t3不是一个新的对象,匿名类会执行析构函数
return 0;
}
1 GCC编译执行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
拷贝函数的调用时机,第1种和第2种
a=1 b=2构造函数初始化, 有2个参数
执行对象的成员函数 a=1 b=2
第1次调用拷贝构造函数a=2 b=3
执行对象的成员函数 a=2 b=3
第2次调用拷贝构造函数a=3 b=4
执行对象的成员函数 a=3 b=4
a=3 b=4析构函数被调用
a=2 b=3析构函数被调用
a=1 b=2析构函数被调用
a=4 b=5构造函数初始化, 有2个参数
第3次调用拷贝构造函数a=5 b=6
拷贝函数的调用时机,第3种
a=5 b=6析构函数被调用
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
a=11 b=12构造函数初始化, 有2个参数
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
a=7 b=8析构函数被调用
a=7 b=8析构函数被调用
a=7 b=8析构函数被调用
a=4 b=5析构函数被调用
chunli@Linux:~/c++$
2 VS环境编译执行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
拷贝函数的调用时机,第1种和第2种
a=1 b=2构造函数初始化, 有2个参数
执行对象的成员函数 a=1 b=2
第1次调用拷贝构造函数a=2 b=3
执行对象的成员函数 a=2 b=3
第2次调用拷贝构造函数a=3 b=4
执行对象的成员函数 a=3 b=4
a=3 b=4析构函数被调用
a=2 b=3析构函数被调用
a=1 b=2析构函数被调用
a=4 b=5构造函数初始化, 有2个参数
第3次调用拷贝构造函数a=5 b=6
拷贝函数的调用时机,第3种
a=5 b=6析构函数被调用
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
第4次调用拷贝构造函数a=8 b=9
a=7 b=8析构函数被调用
a=11 b=12构造函数初始化, 有2个参数
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
第5次调用拷贝构造函数a=8 b=9
a=7 b=8析构函数被调用
a=8 b=9析构函数被调用
a=8 b=9析构函数被调用
a=8 b=9析构函数被调用
a=4 b=5析构函数被调用
在拷贝构造函数里面 可以直接访问私有成员属性
chunli@Linux:~$ cat main1.cpp
#include <iostream>
#include <string.h>
using namespace std;
class Str
{
public:
Str(const char *p = NULL)
{
if(p == NULL)
{
this->p = NULL;
this->len = 0;
}
else
{
this->len = strlen(p);
this->p = new char[this->len +1 ];
strncpy(this->p,p,this->len);
this->p[this->len] = '\0';
}
}
Str(const Str &from)
{
this->len = from.len;
this->p = new char[this->len +1];
strncpy(this->p,from.p,this->len);
this->p[this->len] = '\0';
}
void print()
{
if(this->p != NULL)
{
cout<<this->p<<endl;
}
}
private:
int len;
char *p;
};
int main()
{
Str s1; s1.print();
Str s2("Hello World!"); s2.print();
Str s3 = "Hello Linux!"; s3.print();
Str s4 = s3; s4.print();
return 0;
}
chunli@Linux:~$ g++ -o run main1.cpp && ./run
Hello World!
Hello Linux!
Hello Linux!
chunli@Linux:~$
【关于默认构造函数】
默认构造函数:
二个特殊的构造函数
1)默认无参构造函数
当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空
2)默认拷贝构造函数
当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制
【构造函数调用规则研究 】
1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数
4 )默认拷贝构造函数成员变量简单赋值
总结:只要你写了构造函数,那么你必须用。
1,【当你定义了构造函数,你必须使用】:
先演示没有任何构造函数的情景:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
void fun()
{
cout << "执行对象的成员函数 ";
cout << "a=" << a << " b=" << b << " \n";
}
private:
int a;
int b;
};
int main()
{
Test t1; t1.fun();
return 0;
}
编译运行OK:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
执行对象的成员函数 a=2071403600 b=32765
当类中定义了构造函数,如果不使用,对象初始化就会报错!
示范:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
Test(int _a,int _b)
{
a = _a;
b = _b;
cout << "a=" << a << " b=" << b << "构造函数初始化, 有2个参数\n";
}
void fun()
{
cout << "执行对象的成员函数 ";
cout << "a=" << a << " b=" << b << " \n";
}
private:
int a;
int b;
};
int main()
{
Test t1; t1.fun();//定义一个对象
return 0;
}
编译运行:直接报错
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
main.cpp: In function ‘int main()’:
main.cpp:43:7: error: no matching function for call to ‘Test::Test()’
Test t1; t1.fun();
^
手动定义一个空的构造函数,编译通过!
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
Test()
{
}
void fun()
{
cout << "执行对象的成员函数 ";
cout << "a=" << a << " b=" << b << " \n";
}
private:
int a;
int b;
};
int main()
{
Test t1; t1.fun();
return 0;
}
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
执行对象的成员函数 a=89452272 b=32765
chunli@Linux:~/c++$
【结论】
在类的定义时,只要写了构造函数,编译器就不会再自动为你定义无参的构造函数,这个构造函数必须要使用!
构造析构阶段性总结
1)构造函数是C++中用于初始化对象状态的特殊函数
2)构造函数在对象创建时自动被调用
3)构造函数和普通成员函数都遵循重载规则
4)拷贝构造函数是对象正确初始化的重要保证
5)必要的时候,必须手工编写拷贝构造函数
【浅拷贝 与 深拷贝】
浅拷贝 示范,程序编译通过,运行就死掉,
因为释放已经释放过的内存,所以死掉
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test
{
public:
Test(const char *str)
{
cout << "I'm init....\n";
len = strlen(str);
p = (char *)malloc(len +1);
strcpy(p,str);
*(p + len) = '\0';
}
~Test()
{
if(p != NULL)
{
free(p);
p = NULL;
len = 0;
}
cout << "I'm Free!\n";
}
void show()
{
cout << p <<"\n";
}
private:
char *p;
int len;
};
void fun1()
{
Test t1("hello world"); t1.show();
Test t2(t1); t2.show();
}
int main()
{
fun1();
return 0;
}
编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
I'm init....
hello world
hello world
I'm Free!
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001a93010 ***
Aborted (core dumped)
chunli@Linux:~/c++$
gdb调试:
chunli@Linux:~/c++$ gdb ./a.out
(gdb) run
(gdb) where
#5 0x0000000000400b35 in Test::~Test (this=0x7fffffffead0, __in_chrg=<optimized out>) at main.cpp:23
会看到 at main.cpp:23
这一行就是 free(p);
手动写一个拷贝构造函数吗,完成深拷贝:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test
{
public:
Test(const char *str)
{
cout << "I'm init....\n";
len = strlen(str);
p = (char *)malloc(len +1);
strcpy(p,str);
*(p + len) = '\0';
}
Test(const Test &obj) // 当外部调用 Test t2(t1);
{
//深度拷贝
len = obj.len;
p = (char *)malloc(len +1);;
strcpy(p,obj.p);
*(p + len) = '\0';
}
~Test()
{
if(p != NULL)
{
free(p);
p = NULL;
len = 0;
}
cout << "I'm Free!\n";
}
void show()
{
cout << p <<"\n";
}
private:
char *p;
int len;
};
void fun1()
{
Test t1("hello world"); t1.show();
Test t2(t1); t2.show();
//Test t3 = t1; =等号也是浅拷贝
}
int main()
{
fun1();
return 0;
}
编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
I'm init....
hello world
hello world
I'm Free!
I'm Free!
还是会存在宕掉的可能:
Test t1("hello world!");t1.show();
Test t2("hello Linux!");t2.show();
t1 = t2;//=等号也是浅拷贝
这样会再次导致一片内存两次free,继续宕机!
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test
{
public:
Test(const char *str)
{
cout << "I'm init....\n";
len = strlen(str);
p = (char *)malloc(len +1);
strcpy(p,str);
*(p + len) = '\0';
}
Test(const Test &obj) // 当外部调用 Test t2(t1);
{
//深度拷贝
len = obj.len;
p = (char *)malloc(len +1);;
strcpy(p,obj.p);
*(p + len) = '\0';
cout << "I'm in copy \n";
}
~Test()
{
if(p != NULL)
{
free(p);
p = NULL;
len = 0;
}
cout << "I'm Free!\n";
}
void show()
{
cout << p <<"\n";
}
private:
char *p;
int len;
};
void fun1()
{
Test t1("hello world!"); t1.show();
Test t2("hello Linux!"); t2.show();
t1 = t2; //=等号也是浅拷贝
}
int main()
{
fun1();
return 0;
}
编译通过,运行出错!
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
I'm init....
hello world!
I'm init....
hello Linux!
I'm Free!
*** Error in `./a.out': double free or corruption (fasttop): 0x000000000249b030 ***
Aborted (core dumped)
【gdb调试】:
chunli@Linux:~/c++$ gdb ./a.out
(gdb) run
(gdb) where
#5 0x0000000000400b43 in Test::~Test (this=0x7fffffffead0, __in_chrg=<optimized out>) at main.cpp:32
32行就是 free(p);
构造函数 初始化 列表:
1,首先会执行被组合对象的构造函数
2,如果组成对象有多个,安照对象定义的顺序初始化,而不是按照列表的顺序
3,被组合对象的析构顺序与构造顺序相反
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test1
{
public:
Test1(int _a)
{
a = _a;
cout << a << " Test1 I'm init....\n";
}
~Test1()
{
cout << a << " Test1 I'm Free!\n";
}
private:
int a;
};
class Test2
{
public:
//在初始化参数的同时,将Test1对象完成初始化
Test2(int _a):t11(1),t12(2)
{
a = _a;
cout << "Test2 1 I'm init....\n";
}
//经典用法,参数传递
Test2(int _a,int _b,int _c,int _d):t11(_c),t12(_d)
{
a = _a;
b = _b;
cout << "Test2 2 I'm init....\n";
}
~Test2()
{
cout << "Test2 I'm Free!\n";
}
private:
int a;
int b;
Test1 t11;
Test1 t12;
};
int main()
{
//1 构造函数的初始化列表 解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//新的语法 Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
Test2 t22(1,3,1,4);
return 0;
}
chunli@Linux:~/c++$ g++ -Wall -g main.cpp && ./a.out
1 Test1 I'm init....
4 Test1 I'm init....
Test2 2 I'm init....
Test2 I'm Free!
4 Test1 I'm Free!
1 Test1 I'm Free!
chunli@Linux:~/c++$
如果类的属性有const这么一个属性,必须在构造函数的初始化列表中
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test1
{
public:
Test1(int _a)
{
a = _a;
cout << a << " Test1 I'm init....\n";
}
~Test1()
{
cout << a << " Test1 I'm Free!\n";
}
private:
int a;
};
class Test2
{
public:
//在初始化参数的同时,将Test1对象完成初始化
Test2(int _a):t11(1),t12(2),c(3)
{
a = _a;
cout << "Test2 1 I'm init....\n";
}
//经典用法,参数传递
Test2(int _a,int _b,int _c,int _d):t11(_c),t12(_d),c(_d)
{
a = _a;
b = _b;
cout << "Test2 2 I'm init....\n";
}
~Test2()
{
cout << "Test2 I'm Free!\n";
}
private:
int a;
int b;
const int c;
Test1 t11;
Test1 t12;
};
int main()
{
//1 构造函数的初始化列表 解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//新的语法 Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
Test2 t22(1,3,1,4);
return 0;
}
编译运行:
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
1 Test1 I'm init....
4 Test1 I'm init....
Test2 2 I'm init....
Test2 I'm Free!
4 Test1 I'm Free!
1 Test1 I'm Free!
构造函数,拷贝函数,析构函数综合练习题,根据程序,写出屏幕输出:
【提醒:】
对象中包含有其他对象时,先初始化别人,再初始化自己
对象中包含有其他对象时,先析构自己,再析构别人
chunli@Linux:~/c++$ cat 123.cpp
//对象做函数参数
//1 研究拷贝构造
//2 研究构造函数,析构函数的调用顺序
//总结 构造和析构的调用顺序
#include "iostream"
#include "stdio.h"
using namespace std;
class ABCD
{
public:
ABCD(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
printf("ABCD() construct, a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
~ABCD()
{
printf("~ABCD() construct,a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
int getA()
{
return this->a;
}
protected:
private:
int a;
int b;
int c;
};
class MyE
{
public:
MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
{
cout<<"MyD()"<<endl;
}
~MyE()
{
cout<<"~MyD()"<<endl;
}
MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
{
printf("MyD(const MyD & obj)\n");
}
protected:
//private:
public:
ABCD abcd1; //c++编译器不知道如何构造abc1
ABCD abcd2;
const int m;
};
int doThing(MyE mye1)
{
printf("doThing() mye1.abc1.a:%d \n", mye1.abcd1.getA());
return 0;
}
int run2()
{
MyE myE;
doThing(myE);
return 0;
}
//
int run3()
{
printf("run3 start..\n");
//ABCD(400, 500, 600); //临时对象的生命周期
ABCD abcd = ABCD(100, 200, 300);
//若直接调用构造函数呢?
//想调用构造函数对abc对象进行再复制,可以吗?
//在构造函数里面调用另外一个构造函数,会有什么结果?
printf("run3 end\n");
return 0;
}
int main()
{
run2();
//run3();
return 0;
}
先不要看答案,自己分析一下【注意子对象的构造 与 析构 顺序!】
chunli@Linux:~/c++$ g++ 123.cpp && ./a.out
ABCD() construct, a:1,b:2,c:3
ABCD() construct, a:4,b:5,c:6
MyD()
ABCD() construct, a:7,b:8,c:9
ABCD() construct, a:10,b:11,c:12
MyD(const MyD & obj)
doThing() mye1.abc1.a:7
~MyD()
~ABCD() construct,a:10,b:11,c:12
~ABCD() construct,a:7,b:8,c:9
~MyD()
~ABCD() construct,a:4,b:5,c:6
~ABCD() construct,a:1,b:2,c:3
chunli@Linux:~/c++$
匿名对象的生命周期 强化训练:
分析下列程序的输出结果:
chunli@Linux:~/c++$ cat 123.cpp
#include "iostream"
#include "stdio.h"
using namespace std;
class ABCD
{
public:
ABCD(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
printf("ABCD() construct, a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
~ABCD()
{
printf("~ABCD() construct,a:%d,b:%d,c:%d \n", this->a, this->b, this->c);
}
int getA()
{
return this->a;
}
protected:
private:
int a;
int b;
int c;
};
class MyE
{
public:
MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
{
cout<<"MyD()"<<endl;
}
~MyE()
{
cout<<"~MyD()"<<endl;
}
MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
{
printf("MyD(const MyD & obj)\n");
}
protected:
public:
ABCD abcd1; //c++编译器不知道如何构造abc1
ABCD abcd2;
const int m;
};
int run2()
{
printf("run2 start..\n");
ABCD(400, 500, 600); //临时对象的生命周期
printf("run2 end\n");
return 0;
}
int run3()
{
printf("run3 start..\n");
ABCD abcd = ABCD(100, 200, 300); //这个动作不会调用拷贝构造函数
printf("run3 end\n");
return 0;
}
int main()
{
run2();
cout << "\n\n";
run3();
return 0;
}
编译运行:
chunli@Linux:~/c++$ g++ 123.cpp && ./a.out
run2 start..
ABCD() construct, a:400,b:500,c:600
~ABCD() construct,a:400,b:500,c:600
run2 end
run3 start..
ABCD() construct, a:100,b:200,c:300
run3 end
~ABCD() construct,a:100,b:200,c:300
在构造函数中调用构造函数 是一种危险的行为:
chunli@Linux:~/c++$ cat 123.cpp
#include "iostream"
#include "stdio.h"
using namespace std;
//构造中调用构造是危险的行为
class MyTest
{
public:
MyTest(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
}
MyTest(int a, int b)
{
this->a = a;
this->b = b;
MyTest(a, b, 100); //产生新的匿名对象
}
~MyTest()
{
printf("MyTest~:%d, %d, %d\n", a, b, c);
}
protected:
private:
int a;
int b;
int c;
public:
int getC() const { return c; }
void setC(int val) { c = val; }
};
int main()
{
MyTest t1(1, 2);
printf("c:%d", t1.getC()); //请问c的值是?
return 0;
}
看分析图:
编译运行:
chunli@Linux:~/c++$ g++ 123.cpp && ./a.out
MyTest~:1, 2, 100
c:4196128MyTest~:1, 2, 4196128
C++ new delete
1,基础类型:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
int *p1 = (int *)malloc(sizeof(int));
*p1 = 10;
cout << *p1 << endl;
free(p1);
int *p2 = new int;
*p2 = 20;
cout << *p2 << endl;
detele p2;
//分配的同时初始化
int *p3 = new int(30);
cout << *p3 << endl;
delete p3;
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
10
20
30
数组类型的建立与释放:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
int *p1 = new int[10];
p1[1] = 1;
delete [] p1; //数组的释放
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
chunli@Linux:~/c++$
new delete 动态创建对象
C 与 C++ 的差异
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
using namespace std;
class Test
{
public:
Test(int _a)
{
a = _a;
cout << a << " init \n";
}
~Test()
{
cout <<a<< " free \n";
}
private:
int a;
};
int main()
{
//分配对象new delete
//相同 和 不同的地方 new能执行类型构造函数 delete操作符 能执行类的析构函数
//C
Test *p1 =(Test *)malloc(sizeof(Test));
free(p1);
//C++
Test *p2 = new Test(10);
delete p2;
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
10 init
10 free
new delete深入
【结论】
malloc只会分配内存的大小,不会调用类的构造函数,free也不会调用类的析构函数
但是new会调用类的构造函数,完成类的初始化,delete会调用类的析构函数
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
using namespace std;
class Test
{
public:
Test(int _a)
{
a = _a;
cout << a << " init \n";
}
~Test()
{
cout <<a<< " free \n";
}
private:
int a;
};
int main()
{
//malloc只会分配内存的大小,不会调用类的构造函数,free也不会调用类的析构函数
//但是new会调用类的构造函数,完成类的初始化,delete会调用类的析构函数
// malloc -- > delete
int *p1 = (int *)malloc(sizeof(int));
*p1 = 10;
cout << *p1 << endl;
delete p1;
// new -- > free
int *p2 = new int(30);
cout << *p2 << endl;
free(p2);
// new -- > free
int *p3 = new int[10];
p3[1] = 1;
free(p3);
// malloc -- > delete 触发析构
Test *p4 =(Test *)malloc(sizeof(Test));
delete p4;
// new -- > free 触发构造
Test *p5 = new Test(10);
free(p5);
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
10
30
0 free
10 init
类中静态成员,所有对象都共用:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
using namespace std;
class Test
{
public:
void print_c()
{
cout << c << endl;
}
void add_c()
{
c += 1;
}
Test(int _a)
{
a = _a;
//cout << a << " init \n";
}
~Test()
{
//cout <<a<< " free \n";
}
private:
int a;
int b;
static int c;
};
int Test::c = 10;
int main()
{
Test t1(1); t1.add_c(); t1.print_c();
Test t2(2); t2.add_c(); t2.print_c();
Test t3(3); t3.add_c(); t3.print_c();
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
11
12
13
类中静态成员函数的基本调用:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
using namespace std;
class Test
{
public:
static void print_c()
{
cout << c << endl;
}
void add_c()
{
c += 1;
}
Test(int _a)
{
a = _a;
//cout << a << " init \n";
}
~Test()
{
//cout <<a<< " free \n";
}
private:
int a;
int b;
static int c;
};
int Test::c = 10;
int main()
{
Test t1(1); t1.add_c();
//静态成员函数的调用方法
t1.print_c();
Test::print_c();
return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
11
11
在类中静态函数中不能使用普通成员变量,普通成员变量
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
using namespace std;
class Test
{
public:
static void print_c()
{
cout << c << endl;
cout << a << endl;
}
void add_c()
{
c += 1;
}
Test(int _a)
{
a = _a;
}
~Test()
{
}
private:
int a;
static int c;
};
int Test::c = 10;
int main()
{
Test t1(1); t1.add_c();
//静态成员函数的调用方法
t1.print_c();
Test::print_c();
return 0;
}
编译就报错:
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
main.cpp: In static member function ‘static void Test::print_c()’:
main.cpp:27:7: error: invalid use of member ‘Test::a’ in static member function
int a;
^
面向对象模型 初探:
前言
C++对象模型可以概括为以下2部分:
1. 语言中直接支持面向对象程序设计的部分,主要涉及如构造函数、析构函数、虚函数、继承(单继承、多继承、虚继承)、多态等等。
2. 对于各种支持的底层实现机制。
在c语言中,“数据”和“处理数据的操作(函数)”是分开来声明的,也就是说,语言本身并没有支持“数据和函数”之间的关联性。在c++中,通过抽象数据类型(abstractdata type,ADT),在类中定义数据和函数,来实现数据和函数直接的绑定。
概括来说,在C++类中有两种成员数据:static、nonstatic;三种成员函数:static、nonstatic、virtual。
C++中的class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类。从计算机的角度,程序依然由数据段和代码段构成。
C++编译器如何完成面向对象理论到计算机程序的转化?
换句话:C++编译器是如何管理类、对象、类和对象之间的关系
具体的说:具体对象调用类中的方法,那,c++编译器是如何区分,是那个具体的类,调用这个方法那?
根据程序,写出屏幕输出结果:
chunli@Linux:~/c++$ cat main.cpp
#include "iostream"
#include "stdio.h"
using namespace std;
class C1
{
public:
int i; //4
int j; //4
int k; //4
protected:
private:
}; //12
class C2
{
public:
int i;
int j;
int k;
static int m; //4
public:
int getK() const { return k; } //4
void setK(int val) { k = val; } //4
protected:
private:
};
struct S1
{
int i;
int j;
int k;
};
struct S2
{
int i;
int j;
int k;
static int m;
};
int main()
{
printf("c1:%ld \n", sizeof(C1));
printf("c2:%ld \n", sizeof(C2));
printf("s1:%ld \n", sizeof(S1));
printf("s2:%ld \n", sizeof(S2));
}
编译运行:
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
c1:12
c2:12
s1:12
s2:12
通过上面的案例,我们可以的得出:
1)C++类对象中的成员变量和成员函数是分开存储的
成员变量:
普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对齐方式
静态成员变量:存储于全局数据区中
成员函数:存储于代码段中。
问题出来了:很多对象共用一块代码?代码是如何区分具体对象的那?
换句话说:int getK() const { return k; },代码是如何区分,具体obj1、obj2、obj3对象的k值?
this指针初步:
chunli@Linux:~/c++$ cat main.cpp
#include <iostream>
#include <stdlib.h>
using namespace std;
class Test
{
public:
Test(int a,int b)
{
this->a = a;
this->b = b;
}
void print_var()
{
cout << a << " " << b << endl;
}
private:
int a;
int b;
};
int main()
{
Test t1(1,3); t1.print_var();
Test t2(5,7); t2.print_var();
return 0;
}
编译运行:
chunli@Linux:~/c++$ g++ -g main.cpp && ./a.out
1 3
5 7
chunli@Linux:~/c++$
1、C++类对象中的成员变量和成员函数是分开存储的。C语言中的内存四区模型仍然有效!
2、C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。
3、静态成员函数、成员变量属于类
静态成员函数与普通成员函数的区别
静态成员函数不包含指向具体对象的指针
普通成员函数包含一个指向具体对象的指针
今天作业:
转载于:https://blog.51cto.com/990487026/1794395