c++入门之类与内存
类作为c++编程的核心,自然我们十分关注其内存分配问题。
这里的这个主题中,我们关注了静态成员,new,delete.还有构造函数和析构函数。
先上代码:
1 # include "iostream" 2 # ifndef STRNGBAD_H_ 3 # define STRNGBAD_H_ 4 class StringBad 5 { 6 private: 7 char *str; 8 int len; 9 static int num_strings; //= 0;//可见除了const 量之外,类内部成员是不能在内部赋初值的 10 public: 11 StringBad(const char * s); 12 StringBad(); 13 ~StringBad(); 14 15 friend std::ostream & operator<<(std::ostream & os, const StringBad & st); 16 }; 17 # endif
这个类声明十分的简洁,我们可以看到,其只有三个成员变量,成员函数也只要构造函数和析构函数,再包含一个友元函数(我们再次强调:友元函数虽然声明在public中,但并不包含在类作用域中)关注一下第 9 行,这里声明了一个静态变量。关于静态变量。需要知道:静态变量只提供一个副本,实际上在内存中只占用一个空间,即就算我们申请了10个StringBad对象,这10个对象本质上会共享1个num_strings变量。
再来看,这个类的定义函数:
# include "cstring" # include "strngbad.h" using std::cout; int StringBad::num_strings = 0; StringBad::StringBad(const char*s) { len = std::strlen(s); str = new char[len + 1]; std::strcpy(str, s); num_strings++; cout << num_strings << ": \"" << str << "\" object created\n"; } StringBad::StringBad() { len = 4; str = new char[4]; std::strcpy(str, "C++"); num_strings++; cout << num_strings << ": \"" << str << "\" default created\n"; } StringBad::~StringBad() { cout << "\"" << str << "\" object created. "; --num_strings; cout << num_strings << "left\n"; delete[] str; } std::ostream & operator<<(std::ostream & os, const StringBad & st) { os << st.str;//注意这里打印的是 str,如果不加str呢 return os; }
我们可以从这个类定义中中,看到静态变量num_strings的作用:统计声明当前作用域中,类对象的个数,当对象产生和销毁的时候,对其动态跟踪。同时,我们注意到:
这个程序使用的c字符串的痕迹很明显(要逐渐体会c风格字符串有何缺陷)。
构造函数中使用了new来动态分配内存,析构函数中使用了delete来释放内存。(要注意new和delete的兼容性,即new使用new[],则delete也应使用delete[])。
下面给出调用该类的代码:
1 # include "iostream" 2 using std::cout; 3 # include "strngbad.h" 4 5 void callme1(StringBad &); 6 void callme2(StringBad); 7 8 int main() 9 { 10 using std::endl; 11 { 12 cout << "Starting an inner block.\n"; 13 StringBad headline1("hello world!"); 14 StringBad headline2("learning forever!"); 15 StringBad sports("i love trvaling!"); 16 cout << "headline1" << headline1 << endl; 17 cout << "headline12"<< headline2 << endl; 18 cout << "sports" << sports << endl; 19 callme1(headline1); 20 cout << "headline1" << headline1 << endl; 21 callme2(headline2); 22 cout << "headline2" << headline2 << endl; 23 cout << "Initialize one object to another:\n"; 24 StringBad sailor = sports; 25 cout << "sailor: " << sailor << endl; 26 } 27 cout << "End of main()\n"; 28 system("pause"); 29 return 0; 30 } 31 32 void callme1(StringBad & rsb) 33 { 34 cout << "String passed by reference:\n"; 35 cout << " \n" << rsb << "\"\n"; 36 } 37 38 void callme2(StringBad sb) 39 { 40 cout << "String passed by rvalue:\n"; 41 cout << " \n" << sb << "\"\n"; 42 }
这里先讨论32行和38行:将对象值作为形参和 对象引用作为形参,到底有何区别?
首先需要明白:当采用按值传递参数的时候,会首先复制原来的类对象,当然,也不是简单的复制。这个复制过程很特殊。比如,我们将这个临时变量定义为A,则将对象B的值传递给函数的时候。过程是:A = B, 更具体的讲,是:A =StringBad(B),这个会发生什么呢,当然是,首先调用构造函数,但很可惜的是,这里的构造函数不是我们定义的构造函数,而是编译器生成的构造函数,因此也没有new这一过程,但是当函数执行完。(意味着函数作用域结束),则意味着要调用析构函数了,而这个析构函数却是我们自己的析构函数。,这是极其容易出错的!!!!