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这一过程,但是当函数执行完。(意味着函数作用域结束),则意味着要调用析构函数了,而这个析构函数却是我们自己的析构函数。,这是极其容易出错的!!!!

 

posted @ 2018-11-13 22:21  少年π  阅读(217)  评论(0编辑  收藏  举报